<commit_before>extern crate libanalysis;
extern crate relative_path;
extern crate test_utils;

use std::{
    collections::HashMap,
    path::{Path},
};

use relative_path::{RelativePath, RelativePathBuf};
use libanalysis::{AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId};
use test_utils::assert_eq_dbg;

struct FileMap(&'static [(u32, &'static str)]);

impl FileMap {
    fn path(&self, id: FileId) -> &'static Path {
        let s = self.0.iter()
            .find(|it| it.0 == id.0)
            .unwrap()
            .1;
        Path::new(s)
    }
}

impl FileResolver for FileMap {
    fn file_stem(&self, id: FileId) -> String {
        self.path(id).file_stem().unwrap().to_str().unwrap().to_string()
    }
    fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
        let path = rel.to_path(self.path(id));
        let path = path.strip_prefix("/").unwrap();
        let path = RelativePathBuf::from_path(&path).unwrap().normalize();
        let &(id, _) = self.0.iter()
            .find(|it| {
                let p = Path::new(it.1).strip_prefix("/").unwrap();
                let p = RelativePathBuf::from_path(p).unwrap();
                path == p
            })?;
        Some(FileId(id))
    }
}

#[test]
fn test_resolve_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let (_handle, token) = JobHandle::new();
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo/mod.rs")
    ]));
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_unresolved_module_diagnostic() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[Diagnostic {
            message: "unresolved module",
            range: [4; 7),
            fix: Some(SourceChange {
                label: "create module",
                source_file_edits: [],
                file_system_edits: [CreateFile { anchor: FileId(1), path: "../foo.rs" }],
                cursor_position: None }) }]"#,
        &diagnostics,
    );
}

#[test]
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo {}".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[]"#,
        &diagnostics,
    );
}

#[test]
fn test_resolve_parent_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let symbols = snap.parent_module(FileId(2));
    assert_eq_dbg(
        r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_resolve_crate_root() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert!(snap.crate_for(FileId(2)).is_empty());

    let crate_graph = CrateGraph {
        crate_roots: {
            let mut m = HashMap::new();
            m.insert(CrateId(1), FileId(1));
            m
        },
    };
    world.set_crate_graph(crate_graph);

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert_eq!(
        snap.crate_for(FileId(2)),
        vec![CrateId(1)],
    );
}
<commit_msg>even less hacks<commit_after>extern crate libanalysis;
extern crate relative_path;
extern crate test_utils;

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

use relative_path::{RelativePath};
use libanalysis::{AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId};
use test_utils::assert_eq_dbg;

struct FileMap(&'static [(u32, &'static str)]);

impl FileMap {
    fn iter<'a>(&'a self) -> impl Iterator<Item=(FileId, &'a RelativePath)> + 'a {
        self.0.iter().map(|&(id, path)| {
            assert!(path.starts_with('/'));
            (FileId(id), RelativePath::new(&path[1..]))
        })
    }

    fn path(&self, id: FileId) -> &RelativePath {
        self.iter()
            .find(|&(it, _)| it == id)
            .unwrap()
            .1
    }
}

impl FileResolver for FileMap {
    fn file_stem(&self, id: FileId) -> String {
        self.path(id).file_stem().unwrap().to_string()
    }
    fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
        let path = self.path(id).join(rel).normalize();
        let id = self.iter().find(|&(_, p)| path == p)?.0;
        Some(id)
    }
}

#[test]
fn test_resolve_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let (_handle, token) = JobHandle::new();
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo/mod.rs")
    ]));
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_unresolved_module_diagnostic() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[Diagnostic {
            message: "unresolved module",
            range: [4; 7),
            fix: Some(SourceChange {
                label: "create module",
                source_file_edits: [],
                file_system_edits: [CreateFile { anchor: FileId(1), path: "../foo.rs" }],
                cursor_position: None }) }]"#,
        &diagnostics,
    );
}

#[test]
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo {}".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[]"#,
        &diagnostics,
    );
}

#[test]
fn test_resolve_parent_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let symbols = snap.parent_module(FileId(2));
    assert_eq_dbg(
        r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_resolve_crate_root() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert!(snap.crate_for(FileId(2)).is_empty());

    let crate_graph = CrateGraph {
        crate_roots: {
            let mut m = HashMap::new();
            m.insert(CrateId(1), FileId(1));
            m
        },
    };
    world.set_crate_graph(crate_graph);

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert_eq!(
        snap.crate_for(FileId(2)),
        vec![CrateId(1)],
    );
}
<|endoftext|>


<commit_before>use std::io::{Listener, Acceptor};
use std::io::net::tcp::{TcpListener, TcpStream};

static EXT_PORT: int = 8443;
static SSH_PORT: int = 22;
static SSL_PORT: int = 9443;

fn keep_copying(mut a: TcpStream, mut b: TcpStream) {
	loop {
		let read = try!(a.read(buf));
		b.write(buf.slice_to(read));
	}
}

fn start_pipe(mut front: TcpStream, port: int, header: i8) {
	let mut back = TcpStream::connect("127.0.0.1", port);
	let mut front_copy = front.clone();
	let mut back_copy = back.clone();

	spawn(proc() {
		keep_copying(front, back);
	});
	spawn(proc() {
		keep_copying(back_copy, front_copy);
	});
}

fn handle_new_connection(mut stream: TcpStream) {
	//stream.set_read_timeout(2000);
	let header = try!(stream.read_byte());
	if header == 22 || header == 128 {
		start_pipe(stream, SSL_PORT);
	}
	else {
		start_pipe(stream, SSH_PORT);
	}
}

fn main() {
    let mut acceptor = TcpListener::bind("127.0.0.1", EXT_PORT).listen().unwrap();
    println!("listening started, ready to accept");
    for stream in acceptor.incoming() {
		match stream {
			Err(e) => { /* connection failed */ }
			Ok(stream) => spawn(proc() {
					// connection succeeded
					handle_new_connection(stream)
					})
		}
    }
	drop(acceptor);
}
<commit_msg>Finished making rust version compile<commit_after>use std::io::{Listener, Acceptor, IoResult};
use std::io::net::tcp::{TcpListener, TcpStream};

static EXT_PORT: u16 = 8443;
static SSH_PORT: u16 = 22;
static SSL_PORT: u16 = 9443;

fn keep_copying(mut a: TcpStream, mut b: TcpStream) -> IoResult<uint> {
	let mut buf = [0u8,..1024];
	loop {
		let read = try!(a.read(buf));
		try!(b.write(buf.slice_to(read)));
	}
}

fn start_pipe(front: TcpStream, port: u16, header: u8) {
	let mut back = match TcpStream::connect("127.0.0.1", port) {
		Err(e) => { println!("Error connecting: {}", e); return; },
		Ok(b) => b
	};
	match back.write_u8(header) {
		Err(e) => { println!("Error writing first byte: {}", e); return }
		Ok(..) => ()
	}
	let front_copy = front.clone();
	let back_copy = back.clone();

	spawn(proc() {
		match keep_copying(front, back) {
			Err(..) => println!("Done"),
			Ok(..) => println!("Unexpected..")
		}
	});
	spawn(proc() {
		match keep_copying(back_copy, front_copy) {
			Err(..) => println!("Done"),
			Ok(..) => println!("Unexpected..")
		}
	});
}

fn handle_new_connection(mut stream: TcpStream) {
	//stream.set_read_timeout(2000);
	let header = match stream.read_byte() {
		Err(..) => -1,
		Ok(b) => b
	};
	if header == 22 || header == 128 {
		start_pipe(stream, SSL_PORT, header);
	}
	else {
		start_pipe(stream, SSH_PORT, header);
	}
}

fn main() {
    let mut acceptor = TcpListener::bind("127.0.0.1", EXT_PORT).listen().unwrap();
    println!("listening started, ready to accept");
    for stream in acceptor.incoming() {
		match stream {
			Err(e) => { /* connection failed */ }
			Ok(stream) => spawn(proc() {
					// connection succeeded
					handle_new_connection(stream);
					})
		}
    }
	drop(acceptor);
}
<|endoftext|>


<commit_before>use bytes::{Buf, BufMut};
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
#[cfg(unix)]
use tokio::net::UnixStream;

#[derive(Debug)]
enum Inner {
    Tcp(TcpStream),
    Unix(UnixStream),
}

/// The standard stream type used by the crate.
///
/// Requires the `runtime` Cargo feature (enabled by default).
#[derive(Debug)]
pub struct Socket(Inner);

impl Socket {
    pub(crate) fn new_tcp(stream: TcpStream) -> Socket {
        Socket(Inner::Tcp(stream))
    }

    #[cfg(unix)]
    pub(crate) fn new_unix(stream: UnixStream) -> Socket {
        Socket(Inner::Unix(stream))
    }
}

impl AsyncRead for Socket {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        match &self.0 {
            Inner::Tcp(s) => s.prepare_uninitialized_buffer(buf),
            #[cfg(unix)]
            Inner::Unix(s) => s.prepare_uninitialized_buffer(buf),
        }
    }

    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_read(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_read(cx, buf),
        }
    }

    fn poll_read_buf<B>(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut B,
    ) -> Poll<io::Result<usize>>
    where
        Self: Sized,
        B: BufMut,
    {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_read_buf(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_read_buf(cx, buf),
        }
    }
}

impl AsyncWrite for Socket {
    fn poll_write(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_write(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_write(cx, buf),
        }
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_flush(cx),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_flush(cx),
        }
    }

    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_shutdown(cx),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_shutdown(cx),
        }
    }

    fn poll_write_buf<B>(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut B,
    ) -> Poll<io::Result<usize>>
    where
        Self: Sized,
        B: Buf,
    {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_write_buf(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_write_buf(cx, buf),
        }
    }
}
<commit_msg>Fix Windows build?<commit_after>use bytes::{Buf, BufMut};
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
#[cfg(unix)]
use tokio::net::UnixStream;

#[derive(Debug)]
enum Inner {
    Tcp(TcpStream),
    #[cfg(unix)]
    Unix(UnixStream),
}

/// The standard stream type used by the crate.
///
/// Requires the `runtime` Cargo feature (enabled by default).
#[derive(Debug)]
pub struct Socket(Inner);

impl Socket {
    pub(crate) fn new_tcp(stream: TcpStream) -> Socket {
        Socket(Inner::Tcp(stream))
    }

    #[cfg(unix)]
    pub(crate) fn new_unix(stream: UnixStream) -> Socket {
        Socket(Inner::Unix(stream))
    }
}

impl AsyncRead for Socket {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        match &self.0 {
            Inner::Tcp(s) => s.prepare_uninitialized_buffer(buf),
            #[cfg(unix)]
            Inner::Unix(s) => s.prepare_uninitialized_buffer(buf),
        }
    }

    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_read(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_read(cx, buf),
        }
    }

    fn poll_read_buf<B>(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut B,
    ) -> Poll<io::Result<usize>>
    where
        Self: Sized,
        B: BufMut,
    {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_read_buf(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_read_buf(cx, buf),
        }
    }
}

impl AsyncWrite for Socket {
    fn poll_write(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_write(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_write(cx, buf),
        }
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_flush(cx),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_flush(cx),
        }
    }

    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_shutdown(cx),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_shutdown(cx),
        }
    }

    fn poll_write_buf<B>(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut B,
    ) -> Poll<io::Result<usize>>
    where
        Self: Sized,
        B: Buf,
    {
        match &mut self.0 {
            Inner::Tcp(s) => Pin::new(s).poll_write_buf(cx, buf),
            #[cfg(unix)]
            Inner::Unix(s) => Pin::new(s).poll_write_buf(cx, buf),
        }
    }
}
<|endoftext|>


<commit_before>
use std::cmp;
use std::mem;
use std::iter::*;
use image_data::*;
use rayon::prelude::*;

fn minf(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn maxf(a: f32, b: f32) -> f32 { if a > b { a } else { b } }
fn saturate(a: f32) -> f32 { maxf(minf(a, 1.0), 0.0) }
fn fnorm(a: u8) -> f32 { a as f32 / 255.0 }
fn bnorm(a: f32) -> u8 { (saturate(a) * 255.0) as u8 }
fn to_rgb(a: &Rgba) -> Rgb { [a[0], a[1], a[2]] }
fn to_rgba(a: &Rgb) -> Rgba { [a[0], a[1], a[2], 255u8] }

const ALPHA_THRESHOLD: u8 = 128;

fn pixel_dist(a: &Rgba, b: &Rgba) -> u32 {
	fn dist_one(a: u8, b: u8) -> u32 {
		let x = a as i32 - b as i32;
		(x * x) as u32
	}
	dist_one(a[0], b[0]) +
	dist_one(a[1], b[1]) +
	dist_one(a[2], b[2])
}

fn pixel_dist_alpha(a: &Rgba, b: &Rgba) -> u32 {
	if a[3] < ALPHA_THRESHOLD && b[3] < ALPHA_THRESHOLD {
		0
	} else {
		pixel_dist(a, b) + 1
	}
}

fn block_dist<T: Fn(&Rgba, &Rgba) -> u32>(table: &[Rgba; 4], pixels: &Block, dist: T) -> u32 {
	let mut sum = 0u32;
	let mut max = 0u32;
	for pix in pixels.into_iter().rev() {
		let e = (0..4).into_iter().map(|x| dist(&table[x], pix)).min().unwrap();
		sum += e;
		max = cmp::max(max, e);
	}
	sum + max * 16
}

fn build_table(ends: &(Rgb, Rgb)) -> [Rgba; 4] {
	fn build_entry(ends: &(Rgb, Rgb), index: usize) -> Rgba {
		let b = if index < 2 { index * 3 } else { index - 1 } as u16;
		let a = 3 - b;
		[((b * ends.0[0] as u16 + a * ends.1[0] as u16) / 3) as u8,
		 ((b * ends.0[1] as u16 + a * ends.1[1] as u16) / 3) as u8,
		 ((b * ends.0[2] as u16 + a * ends.1[2] as u16) / 3) as u8,
		 255]
	}
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = build_entry(ends, i);
	}
	interps
}

fn build_table_alpha(ends: &(Rgb, Rgb)) -> [Rgba; 4] {
	let mut mid = [0u8; 3];
	for i in 0..3 {
		mid[i] = (((ends.0[i] as u16) + (ends.1[i] as u16)) / 2) as u8;
	}
	[to_rgba(&ends.0), to_rgba(&ends.1), to_rgba(&mid), [0u8; 4]]
}

fn minmax(pixels: &Block) -> (Rgb, Rgb) {
	let mut min = [255u8; 3];
	let mut max = [0u8; 3];
	for p in pixels.into_iter() {
		for i in 0..3 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
		}
	}
	(min, max)
}

fn quantize_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	fn quantize(end: &Rgb) -> Rgb {
		//[end[0] & 0xF8, end[1] & 0xFC, end[2] & 0xF8]
		let bits = [3, 2, 3];
		let mut e = *end;
		for i in 0..3 {
			let q = (e[i] >> bits[i]) << bits[i];
			e[i] = q + ((e[i] >> (bits[i] - 1)) & 0x01);
		}
		e
	}
	(quantize(&ends.0), quantize(&ends.1))
}

fn extrapolate_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	let mut a = ends.0;
	let mut b = ends.1;
	for i in 0..3 {
		let f = (fnorm(ends.0[i]) - fnorm(ends.1[i])) * 1.5;
		a[i] = bnorm(fnorm(ends.0[i]) - f);
		b[i] = bnorm(fnorm(ends.1[i]) + f);
	}
	(a, b)
}

fn endpoints_sorted(ends: &(Rgb, Rgb)) -> bool {
	encode_endpoint(&ends.0) < encode_endpoint(&ends.1)
}

fn sort_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	if endpoints_sorted(ends) {
		*ends
	} else {
		(ends.1, ends.0)
	}
}

fn sort_quantize_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	quantize_endpoints(&sort_endpoints(ends))
}

fn build_endpoints(pixels: &Block, quality: u8) -> Vec<(Rgb, Rgb)> {
	let (min, max) = quantize_endpoints(&minmax(pixels));

	let mut out = Vec::new();
	out.push((min, max));

	if quality == 255 {
		for i in 0..3 {
			for o in (min[i] + 1)..max[i] {
				let mut min = min;
				min[i] = o;

				for i in 0..3 {
					for o in (min[i] + 1)..max[i] {
						let mut max = max;
						max[i] = o;

						out.push((min, max));
					}
				}
			}
		}
	} if quality > 0 && min != max {
		for i in 0..(BLOCK_PIXELS - 1) {
			for j in (i + 1)..BLOCK_PIXELS {

				let px = (to_rgb(&pixels[i]), to_rgb(&pixels[j]));
				let qpx = sort_quantize_endpoints(&px);
				out.push(qpx);

				if quality > 1 {
					let extrapolated = sort_quantize_endpoints(&extrapolate_endpoints(&px));
					out.push(extrapolated);

					if quality > 2 {
						let e = (extrapolated.0, max);
						if endpoints_sorted(&e) {
							out.push(e);
						}

						let e = (min, extrapolated.1);
						if endpoints_sorted(&e) {
							out.push(e);
						}

						if quality > 3 {
							out.push(sort_endpoints(&(extrapolated.0, qpx.1)));
							out.push(sort_endpoints(&(qpx.0, extrapolated.1)));
						}
					}
				}

			}
		}
	}

	out
}

fn encode_endpoint(end: &Rgb) -> u16 {
	((end[0] as u16 >> 3) << 11) |
	((end[1] as u16 >> 2) << 5) |
	(end[2] as u16 >> 3)
}

fn encode_pixels<T: Fn(&Rgba, &Rgba) -> u32>(table: &[Rgba; 4], pixels: &Block, dist: T) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn encode_block(pixels: &Block, quality: u8) -> u64 {
	let alpha = pixels.into_iter().any(|p| p[3] < ALPHA_THRESHOLD);

	if alpha {
		let (ends, table) =
			build_endpoints(pixels, quality).iter()
			.map(|ends| (*ends, build_table_alpha(ends)))
			.min_by_key(|tbl| block_dist(&tbl.1, pixels, pixel_dist_alpha)).unwrap();

		let min = encode_endpoint(&ends.1) as u64;
		let max = encode_endpoint(&ends.0) as u64;

		let encoded = encode_pixels(&table, &pixels, pixel_dist_alpha) as u64;
		(encoded << 32) | (min << 16) | max

	} else {
		let (ends, table) =
			build_endpoints(pixels, quality).iter()
			.map(|ends| (*ends, build_table(ends)))
			.min_by_key(|tbl| block_dist(&tbl.1, pixels, pixel_dist)).unwrap();

		let min = encode_endpoint(&ends.0) as u64;
		let max = encode_endpoint(&ends.1) as u64;

		let encoded = encode_pixels(&table, &pixels, pixel_dist) as u64;
		(encoded << 32) | (min << 16) | max
	}
}

pub fn encode(image: &ImageData, quality: u8) -> Result<Vec<u8>, ()> {
	if image.size.0 % BLOCK_SIZE != 0 || image.size.1 % BLOCK_SIZE != 0 {
		return Err(());
	}

	let blocks = image.blocks().count();

    let mut out = Vec::with_capacity(blocks);
	(0..blocks).into_par_iter()
		.map(|i| {
			encode_block(&image.blocks().nth(i).unwrap(), quality)
		}).collect_into(&mut out);

	unsafe {
		let mut out: Vec<u8> = mem::transmute(out);
		out.set_len(blocks * 8);
		Ok(out)
	}
}
<commit_msg>Faster q=255.<commit_after>
use std::cmp;
use std::mem;
use std::iter::*;
use image_data::*;
use rayon::prelude::*;

fn minf(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn maxf(a: f32, b: f32) -> f32 { if a > b { a } else { b } }
fn saturate(a: f32) -> f32 { maxf(minf(a, 1.0), 0.0) }
fn fnorm(a: u8) -> f32 { a as f32 / 255.0 }
fn bnorm(a: f32) -> u8 { (saturate(a) * 255.0) as u8 }
fn to_rgb(a: &Rgba) -> Rgb { [a[0], a[1], a[2]] }
fn to_rgba(a: &Rgb) -> Rgba { [a[0], a[1], a[2], 255u8] }

const ALPHA_THRESHOLD: u8 = 128;

fn pixel_dist(a: &Rgba, b: &Rgba) -> u32 {
	fn dist_one(a: u8, b: u8) -> u32 {
		let x = a as i32 - b as i32;
		(x * x) as u32
	}
	dist_one(a[0], b[0]) +
	dist_one(a[1], b[1]) +
	dist_one(a[2], b[2])
}

fn pixel_dist_alpha(a: &Rgba, b: &Rgba) -> u32 {
	if a[3] < ALPHA_THRESHOLD && b[3] < ALPHA_THRESHOLD {
		0
	} else {
		pixel_dist(a, b) + 1
	}
}

fn block_dist<T: Fn(&Rgba, &Rgba) -> u32>(table: &[Rgba; 4], pixels: &Block, dist: T) -> u32 {
	let mut sum = 0u32;
	let mut max = 0u32;
	for pix in pixels.into_iter().rev() {
		let e = (0..4).into_iter().map(|x| dist(&table[x], pix)).min().unwrap();
		sum += e;
		max = cmp::max(max, e);
	}
	sum + max * 16
}

fn build_table(ends: &(Rgb, Rgb)) -> [Rgba; 4] {
	fn build_entry(ends: &(Rgb, Rgb), index: usize) -> Rgba {
		let b = if index < 2 { index * 3 } else { index - 1 } as u16;
		let a = 3 - b;
		[((b * ends.0[0] as u16 + a * ends.1[0] as u16) / 3) as u8,
		 ((b * ends.0[1] as u16 + a * ends.1[1] as u16) / 3) as u8,
		 ((b * ends.0[2] as u16 + a * ends.1[2] as u16) / 3) as u8,
		 255]
	}
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = build_entry(ends, i);
	}
	interps
}

fn build_table_alpha(ends: &(Rgb, Rgb)) -> [Rgba; 4] {
	let mut mid = [0u8; 3];
	for i in 0..3 {
		mid[i] = (((ends.0[i] as u16) + (ends.1[i] as u16)) / 2) as u8;
	}
	[to_rgba(&ends.0), to_rgba(&ends.1), to_rgba(&mid), [0u8; 4]]
}

fn minmax(pixels: &Block) -> (Rgb, Rgb) {
	let mut min = [255u8; 3];
	let mut max = [0u8; 3];
	for p in pixels.into_iter() {
		for i in 0..3 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
		}
	}
	(min, max)
}

fn quantize_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	fn quantize(end: &Rgb) -> Rgb {
		//[end[0] & 0xF8, end[1] & 0xFC, end[2] & 0xF8]
		let bits = [3, 2, 3];
		let mut e = *end;
		for i in 0..3 {
			let q = (e[i] >> bits[i]) << bits[i];
			e[i] = q + ((e[i] >> (bits[i] - 1)) & 0x01);
		}
		e
	}
	(quantize(&ends.0), quantize(&ends.1))
}

fn extrapolate_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	let mut a = ends.0;
	let mut b = ends.1;
	for i in 0..3 {
		let f = (fnorm(ends.0[i]) - fnorm(ends.1[i])) * 1.5;
		a[i] = bnorm(fnorm(ends.0[i]) - f);
		b[i] = bnorm(fnorm(ends.1[i]) + f);
	}
	(a, b)
}

fn endpoints_sorted(ends: &(Rgb, Rgb)) -> bool {
	encode_endpoint(&ends.0) < encode_endpoint(&ends.1)
}

fn sort_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	if endpoints_sorted(ends) {
		*ends
	} else {
		(ends.1, ends.0)
	}
}

fn sort_quantize_endpoints(ends: &(Rgb, Rgb)) -> (Rgb, Rgb) {
	quantize_endpoints(&sort_endpoints(ends))
}

fn build_endpoints(pixels: &Block, quality: u8) -> Vec<(Rgb, Rgb)> {
	let (min, max) = quantize_endpoints(&minmax(pixels));

	let mut out = Vec::new();
	out.push((min, max));

	if quality > 0 && min != max {
		if quality == 255 {
			for i in 0..3 {
				let mid = ((min[i] as u16 + max[i] as u16) / 2) as u8;
				for o in (min[i] + 1)..mid {
					let mut min = min;
					min[i] = o;

					for i in 0..3 {
						for o in mid..max[i] {
							let mut max = max;
							max[i] = o;

							out.push((min, max));
						}
					}
				}
			}
		} else {
			for i in 0..(BLOCK_PIXELS - 1) {
				for j in (i + 1)..BLOCK_PIXELS {

					let px = (to_rgb(&pixels[i]), to_rgb(&pixels[j]));
					let qpx = sort_quantize_endpoints(&px);
					out.push(qpx);

					if quality > 1 {
						let extrapolated = sort_quantize_endpoints(&extrapolate_endpoints(&px));
						out.push(extrapolated);

						if quality > 2 {
							let e = (extrapolated.0, max);
							if endpoints_sorted(&e) {
								out.push(e);
							}

							let e = (min, extrapolated.1);
							if endpoints_sorted(&e) {
								out.push(e);
							}

							if quality > 3 {
								out.push(sort_endpoints(&(extrapolated.0, qpx.1)));
								out.push(sort_endpoints(&(qpx.0, extrapolated.1)));
							}
						}
					}
				}
			}
		}
	}

	out
}

fn encode_endpoint(end: &Rgb) -> u16 {
	((end[0] as u16 >> 3) << 11) |
	((end[1] as u16 >> 2) << 5) |
	(end[2] as u16 >> 3)
}

fn encode_pixels<T: Fn(&Rgba, &Rgba) -> u32>(table: &[Rgba; 4], pixels: &Block, dist: T) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn encode_block(pixels: &Block, quality: u8) -> u64 {
	let alpha = pixels.into_iter().any(|p| p[3] < ALPHA_THRESHOLD);

	if alpha {
		let (ends, table) =
			build_endpoints(pixels, quality).iter()
			.map(|ends| (*ends, build_table_alpha(ends)))
			.min_by_key(|tbl| block_dist(&tbl.1, pixels, pixel_dist_alpha)).unwrap();

		let min = encode_endpoint(&ends.1) as u64;
		let max = encode_endpoint(&ends.0) as u64;

		let encoded = encode_pixels(&table, &pixels, pixel_dist_alpha) as u64;
		(encoded << 32) | (min << 16) | max

	} else {
		let (ends, table) =
			build_endpoints(pixels, quality).iter()
			.map(|ends| (*ends, build_table(ends)))
			.min_by_key(|tbl| block_dist(&tbl.1, pixels, pixel_dist)).unwrap();

		let min = encode_endpoint(&ends.0) as u64;
		let max = encode_endpoint(&ends.1) as u64;

		let encoded = encode_pixels(&table, &pixels, pixel_dist) as u64;
		(encoded << 32) | (min << 16) | max
	}
}

pub fn encode(image: &ImageData, quality: u8) -> Result<Vec<u8>, ()> {
	if image.size.0 % BLOCK_SIZE != 0 || image.size.1 % BLOCK_SIZE != 0 {
		return Err(());
	}

	let blocks = image.blocks().count();

    let mut out = Vec::with_capacity(blocks);
	(0..blocks).into_par_iter()
		.map(|i| {
			encode_block(&image.blocks().nth(i).unwrap(), quality)
		}).collect_into(&mut out);

	unsafe {
		let mut out: Vec<u8> = mem::transmute(out);
		out.set_len(blocks * 8);
		Ok(out)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * 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.
 */

use crate::bufext::BufMutExt;
use crate::errors::ProtocolError;
use crate::framing::{Framing, FramingDecoded, FramingEncoded, FramingEncodedFinal};
use crate::thrift_protocol::{MessageType, ProtocolID};
use crate::ttype::TType;
use crate::Result;

/// The maximum recursive depth the skip() function will traverse
const DEFAULT_RECURSION_DEPTH: i32 = 64;

/// Helper type alias to get the pre-finalization encoded type of a protocol frame.
pub type ProtocolEncoded<P> = FramingEncoded<<P as Protocol>::Frame>;

/// Helper type alias to get the final encoded type of a protocol frame
pub type ProtocolEncodedFinal<P> = FramingEncodedFinal<<P as Protocol>::Frame>;

/// Helper type alias to get the buffer type for a frame to be decoded.
pub type ProtocolDecoded<P> = FramingDecoded<<P as Protocol>::Frame>;

/// An instance of Protocol glues a Framing implementation to a serializer
/// (ProtocolWriter) and deserializer (ProtocolReader). It constructs, as
/// needed, a serializer to construct a frame with a given protocol, or a
/// deserializer from a frame into a stream of deserialized objects.
pub trait Protocol: 'static {
    /// Type of the framing implementation
    type Frame: Framing;

    /// Compute the size of a frame for a given protocol. This can be exact or too large, but
    /// must not be too small.
    type Sizer: ProtocolWriter<Final = usize>;

    /// Serialize into a buffer. The buffer is allocated with the size computed by Sizer, so
    /// it must be large enough.
    type Serializer: ProtocolWriter<Final = <<Self::Frame as Framing>::EncBuf as BufMutExt>::Final>;

    /// Set up a deserializer from a frame's buffer.
    type Deserializer: ProtocolReader;

    const PROTOCOL_ID: ProtocolID;

    fn serializer<SZ, SER>(sz: SZ, ser: SER) -> <Self::Serializer as ProtocolWriter>::Final
    where
        SZ: FnOnce(&mut Self::Sizer),
        SER: FnOnce(&mut Self::Serializer);

    fn deserializer(buf: <Self::Frame as Framing>::DecBuf) -> Self::Deserializer;

    fn into_buffer(_: Self::Deserializer) -> <Self::Frame as Framing>::DecBuf;
}

fn skip_inner<P: ProtocolReader + ?Sized>(
    p: &mut P,
    field_type: TType,
    max_depth: i32,
) -> Result<()> {
    if max_depth <= 0 {
        bail_err!(ProtocolError::SkipDepthExceeded)
    }

    match field_type {
        TType::Void => {}
        TType::Bool => {
            p.read_bool()?;
        }
        TType::Byte => {
            p.read_byte()?;
        }
        TType::I16 => {
            p.read_i16()?;
        }
        TType::I32 => {
            p.read_i32()?;
        }
        TType::I64 => {
            p.read_i64()?;
        }
        TType::Double => {
            p.read_double()?;
        }
        TType::Float => {
            p.read_float()?;
        }
        TType::Struct => {
            p.read_struct_begin(|_| ())?;
            loop {
                let (_, type_id, _) = p.read_field_begin(|_| ())?;
                if type_id == TType::Stop {
                    break;
                }
                skip_inner(p, type_id, max_depth - 1)?;
                p.read_field_end()?;
            }
            p.read_struct_end()?;
        }
        TType::Map => {
            let (key_type, value_type, size) = p.read_map_begin()?;
            for _ in 0..size {
                skip_inner(p, key_type, max_depth - 1)?;
                skip_inner(p, value_type, max_depth)?;
            }
            p.read_map_end()?;
        }
        TType::Set => {
            let (elem_type, size) = p.read_set_begin()?;
            for _ in 0..size {
                skip_inner(p, elem_type, max_depth - 1)?;
            }
            p.read_set_end()?;
        }
        TType::List => {
            let (elem_type, size) = p.read_list_begin()?;
            for _ in 0..size {
                skip_inner(p, elem_type, max_depth - 1)?;
            }
            p.read_list_end()?;
        }
        TType::UTF8 => {
            p.read_string()?;
        }
        TType::UTF16 => {
            p.read_string()?;
        }
        TType::String => {
            p.read_binary()?;
        }
        TType::Stream => bail_err!(ProtocolError::StreamUnsupported),
        TType::Stop => bail_err!(ProtocolError::UnexpectedStopInSkip),
    };
    Ok(())
}

/// Trait for emitting a frame formatted in a given protocol
pub trait ProtocolWriter {
    type Final;

    fn write_message_begin(&mut self, name: &str, type_id: MessageType, seqid: u32);
    fn write_message_end(&mut self);
    fn write_struct_begin(&mut self, name: &str);
    fn write_struct_end(&mut self);
    fn write_field_begin(&mut self, name: &str, type_id: TType, id: i16);
    fn write_field_end(&mut self);
    fn write_field_stop(&mut self);
    fn write_map_begin(&mut self, key_type: TType, value_type: TType, size: usize);
    fn write_map_key_begin(&mut self);
    fn write_map_value_begin(&mut self);
    fn write_map_end(&mut self);
    fn write_list_begin(&mut self, elem_type: TType, size: usize);
    fn write_list_value_begin(&mut self);
    fn write_list_end(&mut self);
    fn write_set_begin(&mut self, elem_type: TType, size: usize);
    fn write_set_value_begin(&mut self);
    fn write_set_end(&mut self);
    fn write_bool(&mut self, value: bool);
    fn write_byte(&mut self, value: i8);
    fn write_i16(&mut self, value: i16);
    fn write_i32(&mut self, value: i32);
    fn write_i64(&mut self, value: i64);
    fn write_double(&mut self, value: f64);
    fn write_float(&mut self, value: f32);
    fn write_string(&mut self, value: &str);
    fn write_binary(&mut self, value: &[u8]);

    fn finish(self) -> Self::Final;
}

/// Trait for decoding a frame in a given protocol
pub trait ProtocolReader {
    fn read_message_begin<F, T>(&mut self, method: F) -> Result<(T, MessageType, u32)>
    where
        F: FnOnce(&[u8]) -> T;
    fn read_message_end(&mut self) -> Result<()>;
    fn read_struct_begin<F, T>(&mut self, strukt: F) -> Result<T>
    where
        F: FnOnce(&[u8]) -> T;
    fn read_struct_end(&mut self) -> Result<()>;
    fn read_field_begin<F, T>(&mut self, field: F) -> Result<(T, TType, i16)>
    where
        F: FnOnce(&[u8]) -> T;
    fn read_field_end(&mut self) -> Result<()>;
    fn read_map_begin(&mut self) -> Result<(TType, TType, usize)>;
    fn read_map_key_begin(&mut self) -> Result<()>;
    fn read_map_value_begin(&mut self) -> Result<()>;
    fn read_map_end(&mut self) -> Result<()>;
    fn read_list_begin(&mut self) -> Result<(TType, usize)>;
    fn read_list_value_begin(&mut self) -> Result<()>;
    fn read_list_end(&mut self) -> Result<()>;
    fn read_set_begin(&mut self) -> Result<(TType, usize)>;
    fn read_set_value_begin(&mut self) -> Result<()>;
    fn read_set_end(&mut self) -> Result<()>;
    fn read_bool(&mut self) -> Result<bool>;
    fn read_byte(&mut self) -> Result<i8>;
    fn read_i16(&mut self) -> Result<i16>;
    fn read_i32(&mut self) -> Result<i32>;
    fn read_i64(&mut self) -> Result<i64>;
    fn read_double(&mut self) -> Result<f64>;
    fn read_float(&mut self) -> Result<f32>;
    fn read_string(&mut self) -> Result<String>;
    fn read_binary(&mut self) -> Result<Vec<u8>>;

    /// Skip over the next data element from the provided input Protocol object
    fn skip(&mut self, field_type: TType) -> Result<()> {
        skip_inner(self, field_type, DEFAULT_RECURSION_DEPTH)
    }
}

/// Protocol::serializer wants the same serializer closure passed twice so
/// that it can specialize it for two types: once to compute the size, and a second time to
/// actually serialize the content. This macro helps by taking the factory type and
/// applying the serializer expression twice.
#[macro_export]
macro_rules! serialize {
    ($protocol:ty, $serializer:expr) => {
        <$protocol as Protocol>::serializer($serializer, $serializer)
    };
}

/// Helper for wrapping a response in a message envelope
pub fn write_message<P, F>(p: &mut P, name: &str, messagetype: MessageType, seqid: u32, body: F)
where
    P: ProtocolWriter,
    F: FnOnce(&mut P),
{
    p.write_message_begin(name, messagetype, seqid);
    body(p);
    p.write_message_end();
}
<commit_msg>Fix calling 'serialize!' without Protocol in scope<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * 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.
 */

use crate::bufext::BufMutExt;
use crate::errors::ProtocolError;
use crate::framing::{Framing, FramingDecoded, FramingEncoded, FramingEncodedFinal};
use crate::thrift_protocol::{MessageType, ProtocolID};
use crate::ttype::TType;
use crate::Result;

/// The maximum recursive depth the skip() function will traverse
const DEFAULT_RECURSION_DEPTH: i32 = 64;

/// Helper type alias to get the pre-finalization encoded type of a protocol frame.
pub type ProtocolEncoded<P> = FramingEncoded<<P as Protocol>::Frame>;

/// Helper type alias to get the final encoded type of a protocol frame
pub type ProtocolEncodedFinal<P> = FramingEncodedFinal<<P as Protocol>::Frame>;

/// Helper type alias to get the buffer type for a frame to be decoded.
pub type ProtocolDecoded<P> = FramingDecoded<<P as Protocol>::Frame>;

/// An instance of Protocol glues a Framing implementation to a serializer
/// (ProtocolWriter) and deserializer (ProtocolReader). It constructs, as
/// needed, a serializer to construct a frame with a given protocol, or a
/// deserializer from a frame into a stream of deserialized objects.
pub trait Protocol: 'static {
    /// Type of the framing implementation
    type Frame: Framing;

    /// Compute the size of a frame for a given protocol. This can be exact or too large, but
    /// must not be too small.
    type Sizer: ProtocolWriter<Final = usize>;

    /// Serialize into a buffer. The buffer is allocated with the size computed by Sizer, so
    /// it must be large enough.
    type Serializer: ProtocolWriter<Final = <<Self::Frame as Framing>::EncBuf as BufMutExt>::Final>;

    /// Set up a deserializer from a frame's buffer.
    type Deserializer: ProtocolReader;

    const PROTOCOL_ID: ProtocolID;

    fn serializer<SZ, SER>(sz: SZ, ser: SER) -> <Self::Serializer as ProtocolWriter>::Final
    where
        SZ: FnOnce(&mut Self::Sizer),
        SER: FnOnce(&mut Self::Serializer);

    fn deserializer(buf: <Self::Frame as Framing>::DecBuf) -> Self::Deserializer;

    fn into_buffer(_: Self::Deserializer) -> <Self::Frame as Framing>::DecBuf;
}

fn skip_inner<P: ProtocolReader + ?Sized>(
    p: &mut P,
    field_type: TType,
    max_depth: i32,
) -> Result<()> {
    if max_depth <= 0 {
        bail_err!(ProtocolError::SkipDepthExceeded)
    }

    match field_type {
        TType::Void => {}
        TType::Bool => {
            p.read_bool()?;
        }
        TType::Byte => {
            p.read_byte()?;
        }
        TType::I16 => {
            p.read_i16()?;
        }
        TType::I32 => {
            p.read_i32()?;
        }
        TType::I64 => {
            p.read_i64()?;
        }
        TType::Double => {
            p.read_double()?;
        }
        TType::Float => {
            p.read_float()?;
        }
        TType::Struct => {
            p.read_struct_begin(|_| ())?;
            loop {
                let (_, type_id, _) = p.read_field_begin(|_| ())?;
                if type_id == TType::Stop {
                    break;
                }
                skip_inner(p, type_id, max_depth - 1)?;
                p.read_field_end()?;
            }
            p.read_struct_end()?;
        }
        TType::Map => {
            let (key_type, value_type, size) = p.read_map_begin()?;
            for _ in 0..size {
                skip_inner(p, key_type, max_depth - 1)?;
                skip_inner(p, value_type, max_depth)?;
            }
            p.read_map_end()?;
        }
        TType::Set => {
            let (elem_type, size) = p.read_set_begin()?;
            for _ in 0..size {
                skip_inner(p, elem_type, max_depth - 1)?;
            }
            p.read_set_end()?;
        }
        TType::List => {
            let (elem_type, size) = p.read_list_begin()?;
            for _ in 0..size {
                skip_inner(p, elem_type, max_depth - 1)?;
            }
            p.read_list_end()?;
        }
        TType::UTF8 => {
            p.read_string()?;
        }
        TType::UTF16 => {
            p.read_string()?;
        }
        TType::String => {
            p.read_binary()?;
        }
        TType::Stream => bail_err!(ProtocolError::StreamUnsupported),
        TType::Stop => bail_err!(ProtocolError::UnexpectedStopInSkip),
    };
    Ok(())
}

/// Trait for emitting a frame formatted in a given protocol
pub trait ProtocolWriter {
    type Final;

    fn write_message_begin(&mut self, name: &str, type_id: MessageType, seqid: u32);
    fn write_message_end(&mut self);
    fn write_struct_begin(&mut self, name: &str);
    fn write_struct_end(&mut self);
    fn write_field_begin(&mut self, name: &str, type_id: TType, id: i16);
    fn write_field_end(&mut self);
    fn write_field_stop(&mut self);
    fn write_map_begin(&mut self, key_type: TType, value_type: TType, size: usize);
    fn write_map_key_begin(&mut self);
    fn write_map_value_begin(&mut self);
    fn write_map_end(&mut self);
    fn write_list_begin(&mut self, elem_type: TType, size: usize);
    fn write_list_value_begin(&mut self);
    fn write_list_end(&mut self);
    fn write_set_begin(&mut self, elem_type: TType, size: usize);
    fn write_set_value_begin(&mut self);
    fn write_set_end(&mut self);
    fn write_bool(&mut self, value: bool);
    fn write_byte(&mut self, value: i8);
    fn write_i16(&mut self, value: i16);
    fn write_i32(&mut self, value: i32);
    fn write_i64(&mut self, value: i64);
    fn write_double(&mut self, value: f64);
    fn write_float(&mut self, value: f32);
    fn write_string(&mut self, value: &str);
    fn write_binary(&mut self, value: &[u8]);

    fn finish(self) -> Self::Final;
}

/// Trait for decoding a frame in a given protocol
pub trait ProtocolReader {
    fn read_message_begin<F, T>(&mut self, method: F) -> Result<(T, MessageType, u32)>
    where
        F: FnOnce(&[u8]) -> T;
    fn read_message_end(&mut self) -> Result<()>;
    fn read_struct_begin<F, T>(&mut self, strukt: F) -> Result<T>
    where
        F: FnOnce(&[u8]) -> T;
    fn read_struct_end(&mut self) -> Result<()>;
    fn read_field_begin<F, T>(&mut self, field: F) -> Result<(T, TType, i16)>
    where
        F: FnOnce(&[u8]) -> T;
    fn read_field_end(&mut self) -> Result<()>;
    fn read_map_begin(&mut self) -> Result<(TType, TType, usize)>;
    fn read_map_key_begin(&mut self) -> Result<()>;
    fn read_map_value_begin(&mut self) -> Result<()>;
    fn read_map_end(&mut self) -> Result<()>;
    fn read_list_begin(&mut self) -> Result<(TType, usize)>;
    fn read_list_value_begin(&mut self) -> Result<()>;
    fn read_list_end(&mut self) -> Result<()>;
    fn read_set_begin(&mut self) -> Result<(TType, usize)>;
    fn read_set_value_begin(&mut self) -> Result<()>;
    fn read_set_end(&mut self) -> Result<()>;
    fn read_bool(&mut self) -> Result<bool>;
    fn read_byte(&mut self) -> Result<i8>;
    fn read_i16(&mut self) -> Result<i16>;
    fn read_i32(&mut self) -> Result<i32>;
    fn read_i64(&mut self) -> Result<i64>;
    fn read_double(&mut self) -> Result<f64>;
    fn read_float(&mut self) -> Result<f32>;
    fn read_string(&mut self) -> Result<String>;
    fn read_binary(&mut self) -> Result<Vec<u8>>;

    /// Skip over the next data element from the provided input Protocol object
    fn skip(&mut self, field_type: TType) -> Result<()> {
        skip_inner(self, field_type, DEFAULT_RECURSION_DEPTH)
    }
}

/// Protocol::serializer wants the same serializer closure passed twice so
/// that it can specialize it for two types: once to compute the size, and a second time to
/// actually serialize the content. This macro helps by taking the factory type and
/// applying the serializer expression twice.
#[macro_export]
macro_rules! serialize {
    ($protocol:ty, $serializer:expr) => {
        <$protocol as $crate::Protocol>::serializer($serializer, $serializer)
    };
}

/// Helper for wrapping a response in a message envelope
pub fn write_message<P, F>(p: &mut P, name: &str, messagetype: MessageType, seqid: u32, body: F)
where
    P: ProtocolWriter,
    F: FnOnce(&mut P),
{
    p.write_message_begin(name, messagetype, seqid);
    body(p);
    p.write_message_end();
}
<|endoftext|>


<commit_before>// Copyright 2015 Ted Mielczarek. See the COPYRIGHT
// file at the top-level directory of this distribution.

use std::boxed::Box;
use std::env;
use std::ops::Deref;
use std::panic;
use std::path::Path;

use minidump::*;
use minidump_processor::{
    http_symbol_supplier, simple_symbol_supplier, DwarfSymbolizer, MultiSymbolProvider, Symbolizer,
};

use clap::{crate_version, App, AppSettings, Arg};
use log::error;
use simplelog::{ColorChoice, ConfigBuilder, Level, LevelFilter, TermLogger, TerminalMode};

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let matches = App::new("minidump_stackwalk")
        .version(crate_version!())
        .about("Analyzes minidumps and produces a machine-readable JSON report.")
        .setting(AppSettings::NextLineHelp)
        .setting(AppSettings::ColoredHelp)
        .arg(
            Arg::with_name("human")
                .long_help("Emit a human-readable report instead.

The human-readable report does not have a specified format, and may not have as \
many details as the default JSON format. It is intended for quickly inspecting \
a crash or debugging rust-minidump itself.\n\n\n")
                .long("human")
        )
        .arg(
            Arg::with_name("pretty")
                .help("Pretty-print JSON output.")
                .long("pretty")
        )
        .arg(
            Arg::with_name("verbose")
                .long_help("Set the level of verbosity (off, error (default), warn, info, debug, trace)

The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why \
an unwind happened the way it did, --verbose=trace is very useful (all unwinder logging will \
be prefixed with `unwind:`).\n\n\n")
                .long("verbose")
                .default_value("error")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("raw-json")
                .long_help("An input JSON file with the extra information.

This is a gross hack for some legacy side-channel information that mozilla uses. It will \
hopefully be phased out and deprecated in favour of just using custom streams in the \
minidump itself.\n\n\n")
                .long("raw-json")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("symbols-url")
                .long_help("base URL from which URLs to symbol files can be constructed.

If multiple symbols-url values are provided, they will each be tried in order until \
one resolves.

The server the base URL points to is expected to conform to the Tecken \
symbol server protocol. For more details, see the Tecken docs:

https://tecken.readthedocs.io/en/latest/

Example symbols-url value: https://symbols.mozilla.org/\n\n\n")
                .multiple(true)
                .long("symbols-url")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("symbols-cache")
                .long_help("A directory in which downloaded symbols can be stored.

Symbol files can be very large, so we recommend placing cached files in your \
system's temp directory so that it can garbage collect unused ones for you. \
To this end, the default value for this flag is a `rust-minidump-cache` \
subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).

symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to \
you, don't worry about it, you're probably not doing something that will run afoul of it).
\n\n\n")
                .long("symbols-cache")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("symbols-tmp")
                .long_help("A directory to use as temp space for downloading symbols.

A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without \
race conditions. Files to be added to the cache will be constructed in this location before \
being atomically moved to the cache.

If no path is specified, `std::env::temp_dir()` will be used to improve portability. \
See the rust documentation for how to set that value if you wish to use something other than \
your system's default temp directory.

symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to \
you, don't worry about it, you're probably not doing something that will run afoul of it).
\n\n\n")
                .long("symbols-tmp")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("minidump")
                .help("Path to the minidump file to analyze.")
                .required(true)
                .takes_value(true)
        )
        .arg(
            Arg::with_name("symbols-path")
                .long_help("Path to a symbol file.

If multiple symbols-path values are provided, all symbol files will be merged \
into minidump-stackwalk's symbol database.\n\n\n")
                .multiple(true)
                .takes_value(true)
        )
        .after_help("

Purpose of Symbols:

  Symbols are used for two purposes:

  1. To fill in more information about each frame of the backtraces. (function names, lines, etc.)

  2. To do produce a more *accurate* backtrace. This is primarily accomplished with \
call frame information (CFI), but just knowing what parts of a module maps to actual \
code is also useful!

Supported Symbol Formats:

  Currently only breakpad text symbol files are supported, although we hope to eventually \
support native formats like PDB and DWARF as well.

Breakpad Symbol Files:

  Breakpad symbol files are basically a simplified version of the information found in \
native debuginfo formats. We recommend using a version of dump_syms to generate them.

  See:
    * symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
    * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms

")
        .get_matches();

    let verbosity = match matches.value_of("verbose").unwrap() {
        "off" => LevelFilter::Off,
        "warn" => LevelFilter::Warn,
        "info" => LevelFilter::Info,
        "debug" => LevelFilter::Debug,
        "trace" => LevelFilter::Trace,
        _ => LevelFilter::Error,
    };

    // Init the logger (and make trace logging less noisy)
    let _ = TermLogger::init(
        verbosity,
        ConfigBuilder::new()
            .set_location_level(LevelFilter::Off)
            .set_time_level(LevelFilter::Off)
            .set_thread_level(LevelFilter::Off)
            .set_target_level(LevelFilter::Off)
            .set_level_color(Level::Trace, None)
            .build(),
        TerminalMode::Stderr,
        ColorChoice::Auto,
    );

    // Set a panic hook to redirect to the logger
    panic::set_hook(Box::new(|panic_info| {
        let (filename, line) = panic_info
            .location()
            .map(|loc| (loc.file(), loc.line()))
            .unwrap_or(("<unknown>", 0));
        let cause = panic_info
            .payload()
            .downcast_ref::<String>()
            .map(String::deref)
            .unwrap_or_else(|| {
                panic_info
                    .payload()
                    .downcast_ref::<&str>()
                    .copied()
                    .unwrap_or("<cause unknown>")
            });
        error!("A panic occurred at {}:{}: {}", filename, line, cause);
    }));

    // All options the original minidump-stackwalk has, stubbed out for when we need them:
    let evil_json_path = matches.value_of_os("raw-json").map(Path::new);

    let temp_dir = std::env::temp_dir();

    // Default to env::temp_dir()/rust-minidump-cache
    let symbols_cache = matches
        .value_of_os("symbols-cache")
        .map(|os_str| Path::new(os_str).to_owned())
        .unwrap_or_else(|| temp_dir.join("rust-minidump-cache"));

    // Default to env::temp_dir()
    let symbols_tmp = matches
        .value_of_os("symbols-tmp")
        .map(|os_str| Path::new(os_str).to_owned())
        .unwrap_or(temp_dir);

    let symbols_urls = matches
        .values_of("symbols-url")
        .map(|v| v.map(String::from).collect::<Vec<_>>())
        .unwrap_or_else(Vec::new);

    let pretty = matches.is_present("pretty");
    let human = matches.is_present("human");

    let minidump_path = matches.value_of_os("minidump").map(Path::new).unwrap();
    let symbols_paths = matches
        .values_of_os("symbols-path")
        .map(|v| {
            v.map(|os_str| Path::new(os_str).to_owned())
                .collect::<Vec<_>>()
        })
        .unwrap_or_else(Vec::new);

    if pretty && human {
        eprintln!("Humans must be hideous! (The --pretty and --human flags cannot both be set)");
        std::process::exit(1);
    }

    // Ok now let's do the thing!!!!

    if let Ok(dump) = Minidump::read_path(minidump_path) {
        let mut provider = MultiSymbolProvider::new();

        if !symbols_urls.is_empty() {
            provider.add(Box::new(Symbolizer::new(http_symbol_supplier(
                symbols_paths,
                symbols_urls,
                symbols_cache,
                symbols_tmp,
            ))));
        } else if !symbols_paths.is_empty() {
            provider.add(Box::new(Symbolizer::new(simple_symbol_supplier(
                symbols_paths,
            ))));
        }
        provider.add(Box::new(DwarfSymbolizer::new()));

        match minidump_processor::process_minidump_with_evil(&dump, &provider, evil_json_path) {
            Ok(state) => {
                let mut stdout = std::io::stdout();
                if human {
                    state.print(&mut stdout).unwrap();
                } else {
                    state.print_json(&mut stdout, pretty).unwrap();
                }
            }
            Err(err) => {
                eprintln!("Error processing dump: {:?}", err);
                std::process::exit(1);
            }
        }
    } else {
        eprintln!("Error reading dump");
        std::process::exit(1);
    }
}
<commit_msg>Add --output-file and --log-file flags to minidump-stackwalk<commit_after>// Copyright 2015 Ted Mielczarek. See the COPYRIGHT
// file at the top-level directory of this distribution.

use std::boxed::Box;
use std::env;
use std::fs::File;
use std::io::Write;
use std::ops::Deref;
use std::panic;
use std::path::Path;

use minidump::*;
use minidump_processor::{
    http_symbol_supplier, simple_symbol_supplier, DwarfSymbolizer, MultiSymbolProvider, Symbolizer,
};

use clap::{crate_version, App, AppSettings, Arg};
use log::error;
use simplelog::{
    ColorChoice, ConfigBuilder, Level, LevelFilter, TermLogger, TerminalMode, WriteLogger,
};

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let matches = App::new("minidump_stackwalk")
        .version(crate_version!())
        .about("Analyzes minidumps and produces a machine-readable JSON report.")
        .setting(AppSettings::NextLineHelp)
        .setting(AppSettings::ColoredHelp)
        .arg(
            Arg::with_name("human")
                .long_help("Emit a human-readable report instead.

The human-readable report does not have a specified format, and may not have as \
many details as the default JSON format. It is intended for quickly inspecting \
a crash or debugging rust-minidump itself.\n\n\n")
                .long("human")
        )
        .arg(
            Arg::with_name("pretty")
                .help("Pretty-print JSON output.")
                .long("pretty")
        )
        .arg(
            Arg::with_name("verbose")
                .long_help("Set the level of verbosity (off, error (default), warn, info, debug, trace)

The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why \
an unwind happened the way it did, --verbose=trace is very useful (all unwinder logging will \
be prefixed with `unwind:`).\n\n\n")
                .long("verbose")
                .default_value("error")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("raw-json")
                .long_help("An input JSON file with the extra information.

This is a gross hack for some legacy side-channel information that mozilla uses. It will \
hopefully be phased out and deprecated in favour of just using custom streams in the \
minidump itself.\n\n\n")
                .long("raw-json")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("symbols-url")
                .long_help("base URL from which URLs to symbol files can be constructed.

If multiple symbols-url values are provided, they will each be tried in order until \
one resolves.

The server the base URL points to is expected to conform to the Tecken \
symbol server protocol. For more details, see the Tecken docs:

https://tecken.readthedocs.io/en/latest/

Example symbols-url value: https://symbols.mozilla.org/\n\n\n")
                .multiple(true)
                .long("symbols-url")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("symbols-cache")
                .long_help("A directory in which downloaded symbols can be stored.

Symbol files can be very large, so we recommend placing cached files in your \
system's temp directory so that it can garbage collect unused ones for you. \
To this end, the default value for this flag is a `rust-minidump-cache` \
subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).

symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to \
you, don't worry about it, you're probably not doing something that will run afoul of it).
\n\n\n")
                .long("symbols-cache")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("symbols-tmp")
                .long_help("A directory to use as temp space for downloading symbols.

A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without \
race conditions. Files to be added to the cache will be constructed in this location before \
being atomically moved to the cache.

If no path is specified, `std::env::temp_dir()` will be used to improve portability. \
See the rust documentation for how to set that value if you wish to use something other than \
your system's default temp directory.

symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to \
you, don't worry about it, you're probably not doing something that will run afoul of it).
\n\n\n")
                .long("symbols-tmp")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("output-file")
                .long("output-file")
                .help("Where to write the output to (if unspecified, stdout is used)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("log-file")
                .long("log-file")
                .help("Where to write logs to (if unspecified, stderr is used)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("minidump")
                .help("Path to the minidump file to analyze.")
                .required(true)
                .takes_value(true)
        )
        .arg(
            Arg::with_name("symbols-path")
                .long_help("Path to a symbol file.

If multiple symbols-path values are provided, all symbol files will be merged \
into minidump-stackwalk's symbol database.\n\n\n")
                .multiple(true)
                .takes_value(true)
        )
        .after_help("

Purpose of Symbols:

  Symbols are used for two purposes:

  1. To fill in more information about each frame of the backtraces. (function names, lines, etc.)

  2. To do produce a more *accurate* backtrace. This is primarily accomplished with \
call frame information (CFI), but just knowing what parts of a module maps to actual \
code is also useful!

Supported Symbol Formats:

  Currently only breakpad text symbol files are supported, although we hope to eventually \
support native formats like PDB and DWARF as well.

Breakpad Symbol Files:

  Breakpad symbol files are basically a simplified version of the information found in \
native debuginfo formats. We recommend using a version of dump_syms to generate them.

  See:
    * symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
    * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms

")
        .get_matches();

    let output_file = matches
        .value_of_os("output-file")
        .map(|os_str| Path::new(os_str).to_owned());

    let log_file = matches
        .value_of_os("log-file")
        .map(|os_str| Path::new(os_str).to_owned());

    let verbosity = match matches.value_of("verbose").unwrap() {
        "off" => LevelFilter::Off,
        "warn" => LevelFilter::Warn,
        "info" => LevelFilter::Info,
        "debug" => LevelFilter::Debug,
        "trace" => LevelFilter::Trace,
        _ => LevelFilter::Error,
    };

    // Init the logger (and make trace logging less noisy)
    if let Some(log_path) = log_file {
        let log_file = File::create(log_path).unwrap();
        let _ = WriteLogger::init(
            verbosity,
            ConfigBuilder::new()
                .set_location_level(LevelFilter::Off)
                .set_time_level(LevelFilter::Off)
                .set_thread_level(LevelFilter::Off)
                .set_target_level(LevelFilter::Off)
                .build(),
            log_file,
        )
        .unwrap();
    } else {
        let _ = TermLogger::init(
            verbosity,
            ConfigBuilder::new()
                .set_location_level(LevelFilter::Off)
                .set_time_level(LevelFilter::Off)
                .set_thread_level(LevelFilter::Off)
                .set_target_level(LevelFilter::Off)
                .set_level_color(Level::Trace, None)
                .build(),
            TerminalMode::Stderr,
            ColorChoice::Auto,
        );
    }

    // Set a panic hook to redirect to the logger
    panic::set_hook(Box::new(|panic_info| {
        let (filename, line) = panic_info
            .location()
            .map(|loc| (loc.file(), loc.line()))
            .unwrap_or(("<unknown>", 0));
        let cause = panic_info
            .payload()
            .downcast_ref::<String>()
            .map(String::deref)
            .unwrap_or_else(|| {
                panic_info
                    .payload()
                    .downcast_ref::<&str>()
                    .copied()
                    .unwrap_or("<cause unknown>")
            });
        error!("A panic occurred at {}:{}: {}", filename, line, cause);
    }));

    // All options the original minidump-stackwalk has, stubbed out for when we need them:
    let evil_json_path = matches.value_of_os("raw-json").map(Path::new);

    let temp_dir = std::env::temp_dir();

    // Default to env::temp_dir()/rust-minidump-cache
    let symbols_cache = matches
        .value_of_os("symbols-cache")
        .map(|os_str| Path::new(os_str).to_owned())
        .unwrap_or_else(|| temp_dir.join("rust-minidump-cache"));

    // Default to env::temp_dir()
    let symbols_tmp = matches
        .value_of_os("symbols-tmp")
        .map(|os_str| Path::new(os_str).to_owned())
        .unwrap_or(temp_dir);

    let symbols_urls = matches
        .values_of("symbols-url")
        .map(|v| v.map(String::from).collect::<Vec<_>>())
        .unwrap_or_else(Vec::new);

    let pretty = matches.is_present("pretty");
    let human = matches.is_present("human");

    let minidump_path = matches.value_of_os("minidump").map(Path::new).unwrap();
    let symbols_paths = matches
        .values_of_os("symbols-path")
        .map(|v| {
            v.map(|os_str| Path::new(os_str).to_owned())
                .collect::<Vec<_>>()
        })
        .unwrap_or_else(Vec::new);

    if pretty && human {
        error!("Humans must be hideous! (The --pretty and --human flags cannot both be set)");
        std::process::exit(1);
    }

    // Ok now let's do the thing!!!!

    if let Ok(dump) = Minidump::read_path(minidump_path) {
        let mut provider = MultiSymbolProvider::new();

        if !symbols_urls.is_empty() {
            provider.add(Box::new(Symbolizer::new(http_symbol_supplier(
                symbols_paths,
                symbols_urls,
                symbols_cache,
                symbols_tmp,
            ))));
        } else if !symbols_paths.is_empty() {
            provider.add(Box::new(Symbolizer::new(simple_symbol_supplier(
                symbols_paths,
            ))));
        }
        provider.add(Box::new(DwarfSymbolizer::new()));

        match minidump_processor::process_minidump_with_evil(&dump, &provider, evil_json_path) {
            Ok(state) => {
                let mut stdout;
                let mut output_f;

                let mut output: &mut dyn Write = if let Some(output_path) = output_file {
                    output_f = File::create(output_path).unwrap();
                    &mut output_f
                } else {
                    stdout = std::io::stdout();
                    &mut stdout
                };

                if human {
                    state.print(&mut output).unwrap();
                } else {
                    state.print_json(&mut output, pretty).unwrap();
                }
            }
            Err(err) => {
                error!("Error processing dump: {:?}", err);
                std::process::exit(1);
            }
        }
    } else {
        error!("Error reading dump");
        std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>fn main() {
    let mut s = String::from("hello there!");

    // append to string

    s.push_str(" Nice to meet you!");

    println!("{}", s);

    let s2 = s;
    // s is no longer valid here!
    println!("{}", s);
}
<commit_msg>Add example of cloning<commit_after>fn main() {
    let mut s = String::from("hello there!");

    // append to string

    s.push_str(" Nice to meet you!");

    println!("{}", s);

    let s1 = s.clone();

    // cloning doesn't invalidate original variable
    println!("s: {}, s1: {}", s, s1);

    let s2 = s;

    // s is no longer valid here!
    // println!("{}", s); would fail
}
<|endoftext|>


<commit_before>extern crate centralgithook;
use centralgithook::*;
#[allow(dead_code)]
mod helpers;
extern crate git2;
use std::cell::UnsafeCell;
extern crate tempdir;
use tempdir::TempDir;

struct MockHooks
{
    called: UnsafeCell<String>,
    review_upload_return: ReviewUploadResult,
}

impl MockHooks
{
    fn new() -> Self { MockHooks { called: UnsafeCell::new(String::new()) , review_upload_return: ReviewUploadResult::Central}}

    fn set_called(&self, s: &str)
    {
        unsafe {
            (*self.called.get()).clear();
            (*self.called.get()).push_str(s);
        }
    }

    fn called(&self) -> &str
    {
        unsafe {
            &*self.called.get()
        }
    }
}

impl Hooks for MockHooks
{
    fn review_upload(&self,
                     _scratch: &Scratch,
                     newrev: git2::Object,
                     module: &str)
        -> ReviewUploadResult
    {
        self.set_called(&format!("review_upload(_,{},{})", newrev.id(), module));
        self.review_upload_return.clone()
    }

    fn project_created(&self, _scratch: &Scratch)
    {
        self.set_called(&format!("project_created(_)"));
    }

    fn central_submit(&self, _scratch: &Scratch, newrev: git2::Object)
    {
        self.set_called(&format!("central_submit(_,{})", newrev.id()));
    }
}

#[test]
fn test_hook()
{
    let host = helpers::TestHost::new();
    let td = TempDir::new("cgh_test").expect("folder cgh_test should be created");
    let scratch = Scratch::new(&td.path().join("scratch"), &host);
    let mut hooks = MockHooks::new();

    host.create_project("central");
    let central = helpers::TestRepo::new(&td.path().join("central"));
    central.add_file("foo_module_a/initial_a");
    let head = central.commit("on_branch_tmp");
    central.shell.command(&format!("git remote add origin {}", &host.remote_url("central")));
    central.shell.command("git push origin master");

    hooks.review_upload_return = ReviewUploadResult::Central;

    assert_eq!(0,dispatch(vec![
        format!("ref-update"),
        format!("--refname"), format!("refs/for/master"),
        format!("--newrev"), format!("{}",head),
        format!("--project"), format!("central"),
    ], &hooks, &host, &scratch));

    assert_eq!(hooks.called(), format!("review_upload(_,{},central)", head));

    host.create_project("module");
    let module = helpers::TestRepo::new(&td.path().join("module"));
    module.add_file("foo_module_a/initial_a");
    let head = module.commit("on_branch_tmp");
    module.shell.command(&format!("git remote add origin {}", &host.remote_url("module")));
    module.shell.command("git push origin master");

    hooks.review_upload_return = ReviewUploadResult::Uploaded(git2::Oid::from_str(&head).unwrap());

    assert_eq!(1,dispatch(vec![
        format!("ref-update"),
        format!("--refname"), format!("refs/for/master"),
        format!("--newrev"), format!("{}",head),
        format!("--project"), format!("module"),
    ], &hooks, &host, &scratch));

    assert_eq!(hooks.called(), format!("review_upload(_,{},module)", head));
}
<commit_msg>add test<commit_after>extern crate centralgithook;
use centralgithook::*;
#[allow(dead_code)]
mod helpers;
extern crate git2;
use std::cell::UnsafeCell;
extern crate tempdir;
use tempdir::TempDir;

struct MockHooks
{
    called: UnsafeCell<String>,
    review_upload_return: ReviewUploadResult,
}

impl MockHooks
{
    fn new() -> Self { MockHooks { called: UnsafeCell::new(String::new()) , review_upload_return: ReviewUploadResult::Central}}

    fn set_called(&self, s: &str)
    {
        unsafe {
            (*self.called.get()).clear();
            (*self.called.get()).push_str(s);
        }
    }

    fn called(&self) -> &str
    {
        unsafe {
            &*self.called.get()
        }
    }
}

impl Hooks for MockHooks
{
    fn review_upload(&self,
                     _scratch: &Scratch,
                     newrev: git2::Object,
                     module: &str)
        -> ReviewUploadResult
    {
        self.set_called(&format!("review_upload(_,{},{})", newrev.id(), module));
        self.review_upload_return.clone()
    }

    fn project_created(&self, _scratch: &Scratch)
    {
        self.set_called(&format!("project_created(_)"));
    }

    fn central_submit(&self, _scratch: &Scratch, newrev: git2::Object)
    {
        self.set_called(&format!("central_submit(_,{})", newrev.id()));
    }
}

#[test]
fn test_hook()
{
    let host = helpers::TestHost::new();
    let td = TempDir::new("cgh_test").expect("folder cgh_test should be created");
    let scratch = Scratch::new(&td.path().join("scratch"), &host);
    let mut hooks = MockHooks::new();

    host.create_project("central");
    let central = helpers::TestRepo::new(&td.path().join("central"));
    central.add_file("foo_module_a/initial_a");
    let head = central.commit("on_branch_tmp");
    central.shell.command(&format!("git remote add origin {}", &host.remote_url("central")));
    central.shell.command("git push origin master");

    hooks.review_upload_return = ReviewUploadResult::Central;

    assert_eq!(0,dispatch(vec![
        format!("ref-update"),
        format!("--refname"), format!("refs/for/master"),
        format!("--newrev"), format!("{}",head),
        format!("--project"), format!("central"),
    ], &hooks, &host, &scratch));

    assert_eq!(hooks.called(), format!("review_upload(_,{},central)", head));

    host.create_project("module");
    let module = helpers::TestRepo::new(&td.path().join("module"));
    module.add_file("foo_module_a/initial_a");
    let head = module.commit("on_branch_tmp");
    module.shell.command(&format!("git remote add origin {}", &host.remote_url("module")));
    module.shell.command("git push origin master");

    hooks.review_upload_return = ReviewUploadResult::Uploaded(git2::Oid::from_str(&head).unwrap());

    assert_eq!(1,dispatch(vec![
        format!("ref-update"),
        format!("--refname"), format!("refs/for/master"),
        format!("--newrev"), format!("{}",head),
        format!("--project"), format!("module"),
    ], &hooks, &host, &scratch));

    assert_eq!(hooks.called(), format!("review_upload(_,{},module)", head));

    assert_eq!(0,dispatch(vec![
        format!("change-merged"),
        format!("--refname"), format!("refs/for/master"),
        format!("--commit"), format!("{}",head),
        format!("--project"), format!("central"),
    ], &hooks, &host, &scratch));

    assert_eq!(hooks.called(), format!("central_submit(_,{})", head));
}
<|endoftext|>