<commit_before>use crate::common::util::*;
use std::io::Read;

#[test]
fn test_output_simple() {
    new_ucmd!()
        .args(&["alpha.txt"])
        .succeeds()
        .stdout_only("abcde\nfghij\nklmno\npqrst\nuvwxyz\n");
}

#[test]
fn test_no_options() {
    for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
        // Give fixture through command line file argument
        new_ucmd!()
            .args(&[fixture])
            .succeeds()
            .stdout_is_fixture(fixture);
        // Give fixture through stdin
        new_ucmd!()
            .pipe_in_fixture(fixture)
            .succeeds()
            .stdout_is_fixture(fixture);
    }
}

#[test]
fn test_no_options_big_input() {
    for &n in &[
        0,
        1,
        42,
        16 * 1024 - 7,
        16 * 1024 - 1,
        16 * 1024,
        16 * 1024 + 1,
        16 * 1024 + 3,
        32 * 1024,
        64 * 1024,
        80 * 1024,
        96 * 1024,
        112 * 1024,
        128 * 1024,
    ] {
        let data = vec_of_size(n);
        let data2 = data.clone();
        assert_eq!(data.len(), data2.len());
        new_ucmd!().pipe_in(data).succeeds().stdout_is_bytes(&data2);
    }
}

#[test]
#[cfg(unix)]
fn test_fifo_symlink() {
    use std::fs::OpenOptions;
    use std::io::Write;
    use std::thread;

    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("dir");
    s.fixtures.mkfifo("dir/pipe");
    assert!(s.fixtures.is_fifo("dir/pipe"));

    // Make cat read the pipe through a symlink
    s.fixtures.symlink_file("dir/pipe", "sympipe");
    let proc = s.ucmd().args(&["sympipe"]).run_no_wait();

    let data = vec_of_size(128 * 1024);
    let data2 = data.clone();

    let pipe_path = s.fixtures.plus("dir/pipe");
    let thread = thread::spawn(move || {
        let mut pipe = OpenOptions::new()
            .write(true)
            .create(false)
            .open(pipe_path)
            .unwrap();
        pipe.write_all(&data).unwrap();
    });

    let output = proc.wait_with_output().unwrap();
    assert_eq!(&output.stdout, &data2);
    thread.join().unwrap();
}

#[test]
fn test_directory() {
    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("test_directory");
    s.ucmd()
        .args(&["test_directory"])
        .fails()
        .stderr_is("cat: test_directory: Is a directory");
}

#[test]
fn test_directory_and_file() {
    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("test_directory2");
    for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
        s.ucmd()
            .args(&["test_directory2", fixture])
            .fails()
            .stderr_is("cat: test_directory2: Is a directory")
            .stdout_is_fixture(fixture);
    }
}

#[test]
fn test_three_directories_and_file_and_stdin() {
    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("test_directory3");
    s.fixtures.mkdir("test_directory3/test_directory4");
    s.fixtures.mkdir("test_directory3/test_directory5");
    s.ucmd()
        .args(&[
            "test_directory3/test_directory4",
            "alpha.txt",
            "-",
            "filewhichdoesnotexist.txt",
            "nonewline.txt",
            "test_directory3/test_directory5",
            "test_directory3/../test_directory3/test_directory5",
            "test_directory3",
        ])
        .pipe_in("stdout bytes")
        .fails()
        .stderr_is_fixture("three_directories_and_file_and_stdin.stderr.expected")
        .stdout_is(
            "abcde\nfghij\nklmno\npqrst\nuvwxyz\nstdout bytestext without a trailing newline",
        );
}

#[test]
fn test_output_multi_files_print_all_chars() {
    new_ucmd!()
        .args(&["alpha.txt", "256.txt", "-A", "-n"])
        .succeeds()
        .stdout_only(
            "     1\tabcde$\n     2\tfghij$\n     3\tklmno$\n     4\tpqrst$\n     \
             5\tuvwxyz$\n     6\t^@^A^B^C^D^E^F^G^H^I$\n     \
             7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
             !\"#$%&\'()*+,-./0123456789:;\
             <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
             BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
             M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
             M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
             M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
             M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
             pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
        );
}

#[test]
fn test_numbered_lines_no_trailing_newline() {
    new_ucmd!()
        .args(&["nonewline.txt", "alpha.txt", "-n"])
        .succeeds()
        .stdout_only(
            "     1\ttext without a trailing newlineabcde\n     2\tfghij\n     \
             3\tklmno\n     4\tpqrst\n     5\tuvwxyz\n",
        );
}

#[test]
fn test_stdin_show_nonprinting() {
    for same_param in vec!["-v", "--show-nonprinting"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("\t^@\n");
    }
}

#[test]
fn test_stdin_show_tabs() {
    for same_param in vec!["-T", "--show-tabs"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I\0\n");
    }
}

#[test]
fn test_stdin_show_ends() {
    for same_param in vec!["-E", "--show-ends"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\t\0\n\t")
            .succeeds()
            .stdout_only("\t\0$\n\t");
    }
}

#[test]
fn test_stdin_show_all() {
    for same_param in vec!["-A", "--show-all"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I^@$\n");
    }
}

#[test]
fn test_stdin_nonprinting_and_endofline() {
    new_ucmd!()
        .args(&["-e"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("\t^@$\n");
}

#[test]
fn test_stdin_nonprinting_and_tabs() {
    new_ucmd!()
        .args(&["-t"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("^I^@\n");
}

#[test]
fn test_stdin_squeeze_blank() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .arg(same_param)
            .pipe_in("\n\na\n\n\n\n\nb\n\n\n")
            .succeeds()
            .stdout_only("\na\n\nb\n\n");
    }
}

#[test]
fn test_stdin_number_non_blank() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .arg(same_param)
            .arg("-")
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_non_blank_overrides_number() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_squeeze_blank_before_numbering() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .args(&[same_param, "-n", "-"])
            .pipe_in("a\n\n\nb")
            .succeeds()
            .stdout_only("     1\ta\n     2\t\n     3\tb");
    }
}

/// This tests reading from Unix character devices
#[test]
#[cfg(unix)]
fn test_dev_random() {
    let mut buf = [0; 2048];
    let mut proc = new_ucmd!().args(&["/dev/random"]).run_no_wait();
    let mut proc_stdout = proc.stdout.take().unwrap();
    proc_stdout.read_exact(&mut buf).unwrap();

    let num_zeroes = buf.iter().fold(0, |mut acc, &n| {
        if n == 0 {
            acc += 1;
        }
        acc
    });
    // The probability of more than 512 zero bytes is essentially zero if the
    // output is truly random.
    assert!(num_zeroes < 512);
    proc.kill().unwrap();
}

/// Reading from /dev/full should return an infinite amount of zero bytes.
/// Wikipedia says there is support on Linux, FreeBSD, and NetBSD.
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_dev_full() {
    let mut buf = [0; 2048];
    let mut proc = new_ucmd!().args(&["/dev/full"]).run_no_wait();
    let mut proc_stdout = proc.stdout.take().unwrap();
    let expected = [0; 2048];
    proc_stdout.read_exact(&mut buf).unwrap();
    assert_eq!(&buf[..], &expected[..]);
    proc.kill().unwrap();
}

#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_dev_full_show_all() {
    let mut buf = [0; 2048];
    let mut proc = new_ucmd!().args(&["-A", "/dev/full"]).run_no_wait();
    let mut proc_stdout = proc.stdout.take().unwrap();
    proc_stdout.read_exact(&mut buf).unwrap();

    let expected: Vec<u8> = (0..buf.len())
        .map(|n| if n & 1 == 0 { b'^' } else { b'@' })
        .collect();

    assert_eq!(&buf[..], &expected[..]);
    proc.kill().unwrap();
}

#[test]
#[cfg(unix)]
fn test_domain_socket() {
    use std::io::prelude::*;
    use std::thread;
    use tempdir::TempDir;
    use unix_socket::UnixListener;

    let dir = TempDir::new("unix_socket").expect("failed to create dir");
    let socket_path = dir.path().join("sock");
    let listener = UnixListener::bind(&socket_path).expect("failed to create socket");

    let thread = thread::spawn(move || {
        let mut stream = listener.accept().expect("failed to accept connection").0;
        stream
            .write_all(b"a\tb")
            .expect("failed to write test data");
    });

    new_ucmd!()
        .args(&[socket_path])
        .succeeds()
        .stdout_only("a\tb");

    thread.join().unwrap();
}
<commit_msg>Disable test_no_options_big_input on Windows<commit_after>use crate::common::util::*;
use std::io::Read;

#[test]
fn test_output_simple() {
    new_ucmd!()
        .args(&["alpha.txt"])
        .succeeds()
        .stdout_only("abcde\nfghij\nklmno\npqrst\nuvwxyz\n");
}

#[test]
fn test_no_options() {
    for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
        // Give fixture through command line file argument
        new_ucmd!()
            .args(&[fixture])
            .succeeds()
            .stdout_is_fixture(fixture);
        // Give fixture through stdin
        new_ucmd!()
            .pipe_in_fixture(fixture)
            .succeeds()
            .stdout_is_fixture(fixture);
    }
}

#[test]
#[cfg(unix)]
fn test_no_options_big_input() {
    for &n in &[
        0,
        1,
        42,
        16 * 1024 - 7,
        16 * 1024 - 1,
        16 * 1024,
        16 * 1024 + 1,
        16 * 1024 + 3,
        32 * 1024,
        64 * 1024,
        80 * 1024,
        96 * 1024,
        112 * 1024,
        128 * 1024,
    ] {
        let data = vec_of_size(n);
        let data2 = data.clone();
        assert_eq!(data.len(), data2.len());
        new_ucmd!().pipe_in(data).succeeds().stdout_is_bytes(&data2);
    }
}

#[test]
#[cfg(unix)]
fn test_fifo_symlink() {
    use std::fs::OpenOptions;
    use std::io::Write;
    use std::thread;

    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("dir");
    s.fixtures.mkfifo("dir/pipe");
    assert!(s.fixtures.is_fifo("dir/pipe"));

    // Make cat read the pipe through a symlink
    s.fixtures.symlink_file("dir/pipe", "sympipe");
    let proc = s.ucmd().args(&["sympipe"]).run_no_wait();

    let data = vec_of_size(128 * 1024);
    let data2 = data.clone();

    let pipe_path = s.fixtures.plus("dir/pipe");
    let thread = thread::spawn(move || {
        let mut pipe = OpenOptions::new()
            .write(true)
            .create(false)
            .open(pipe_path)
            .unwrap();
        pipe.write_all(&data).unwrap();
    });

    let output = proc.wait_with_output().unwrap();
    assert_eq!(&output.stdout, &data2);
    thread.join().unwrap();
}

#[test]
fn test_directory() {
    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("test_directory");
    s.ucmd()
        .args(&["test_directory"])
        .fails()
        .stderr_is("cat: test_directory: Is a directory");
}

#[test]
fn test_directory_and_file() {
    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("test_directory2");
    for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
        s.ucmd()
            .args(&["test_directory2", fixture])
            .fails()
            .stderr_is("cat: test_directory2: Is a directory")
            .stdout_is_fixture(fixture);
    }
}

#[test]
fn test_three_directories_and_file_and_stdin() {
    let s = TestScenario::new(util_name!());
    s.fixtures.mkdir("test_directory3");
    s.fixtures.mkdir("test_directory3/test_directory4");
    s.fixtures.mkdir("test_directory3/test_directory5");
    s.ucmd()
        .args(&[
            "test_directory3/test_directory4",
            "alpha.txt",
            "-",
            "filewhichdoesnotexist.txt",
            "nonewline.txt",
            "test_directory3/test_directory5",
            "test_directory3/../test_directory3/test_directory5",
            "test_directory3",
        ])
        .pipe_in("stdout bytes")
        .fails()
        .stderr_is_fixture("three_directories_and_file_and_stdin.stderr.expected")
        .stdout_is(
            "abcde\nfghij\nklmno\npqrst\nuvwxyz\nstdout bytestext without a trailing newline",
        );
}

#[test]
fn test_output_multi_files_print_all_chars() {
    new_ucmd!()
        .args(&["alpha.txt", "256.txt", "-A", "-n"])
        .succeeds()
        .stdout_only(
            "     1\tabcde$\n     2\tfghij$\n     3\tklmno$\n     4\tpqrst$\n     \
             5\tuvwxyz$\n     6\t^@^A^B^C^D^E^F^G^H^I$\n     \
             7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
             !\"#$%&\'()*+,-./0123456789:;\
             <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
             BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
             M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
             M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
             M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
             M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
             pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
        );
}

#[test]
fn test_numbered_lines_no_trailing_newline() {
    new_ucmd!()
        .args(&["nonewline.txt", "alpha.txt", "-n"])
        .succeeds()
        .stdout_only(
            "     1\ttext without a trailing newlineabcde\n     2\tfghij\n     \
             3\tklmno\n     4\tpqrst\n     5\tuvwxyz\n",
        );
}

#[test]
fn test_stdin_show_nonprinting() {
    for same_param in vec!["-v", "--show-nonprinting"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("\t^@\n");
    }
}

#[test]
fn test_stdin_show_tabs() {
    for same_param in vec!["-T", "--show-tabs"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I\0\n");
    }
}

#[test]
fn test_stdin_show_ends() {
    for same_param in vec!["-E", "--show-ends"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\t\0\n\t")
            .succeeds()
            .stdout_only("\t\0$\n\t");
    }
}

#[test]
fn test_stdin_show_all() {
    for same_param in vec!["-A", "--show-all"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I^@$\n");
    }
}

#[test]
fn test_stdin_nonprinting_and_endofline() {
    new_ucmd!()
        .args(&["-e"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("\t^@$\n");
}

#[test]
fn test_stdin_nonprinting_and_tabs() {
    new_ucmd!()
        .args(&["-t"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("^I^@\n");
}

#[test]
fn test_stdin_squeeze_blank() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .arg(same_param)
            .pipe_in("\n\na\n\n\n\n\nb\n\n\n")
            .succeeds()
            .stdout_only("\na\n\nb\n\n");
    }
}

#[test]
fn test_stdin_number_non_blank() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .arg(same_param)
            .arg("-")
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_non_blank_overrides_number() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_squeeze_blank_before_numbering() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .args(&[same_param, "-n", "-"])
            .pipe_in("a\n\n\nb")
            .succeeds()
            .stdout_only("     1\ta\n     2\t\n     3\tb");
    }
}

/// This tests reading from Unix character devices
#[test]
#[cfg(unix)]
fn test_dev_random() {
    let mut buf = [0; 2048];
    let mut proc = new_ucmd!().args(&["/dev/random"]).run_no_wait();
    let mut proc_stdout = proc.stdout.take().unwrap();
    proc_stdout.read_exact(&mut buf).unwrap();

    let num_zeroes = buf.iter().fold(0, |mut acc, &n| {
        if n == 0 {
            acc += 1;
        }
        acc
    });
    // The probability of more than 512 zero bytes is essentially zero if the
    // output is truly random.
    assert!(num_zeroes < 512);
    proc.kill().unwrap();
}

/// Reading from /dev/full should return an infinite amount of zero bytes.
/// Wikipedia says there is support on Linux, FreeBSD, and NetBSD.
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_dev_full() {
    let mut buf = [0; 2048];
    let mut proc = new_ucmd!().args(&["/dev/full"]).run_no_wait();
    let mut proc_stdout = proc.stdout.take().unwrap();
    let expected = [0; 2048];
    proc_stdout.read_exact(&mut buf).unwrap();
    assert_eq!(&buf[..], &expected[..]);
    proc.kill().unwrap();
}

#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_dev_full_show_all() {
    let mut buf = [0; 2048];
    let mut proc = new_ucmd!().args(&["-A", "/dev/full"]).run_no_wait();
    let mut proc_stdout = proc.stdout.take().unwrap();
    proc_stdout.read_exact(&mut buf).unwrap();

    let expected: Vec<u8> = (0..buf.len())
        .map(|n| if n & 1 == 0 { b'^' } else { b'@' })
        .collect();

    assert_eq!(&buf[..], &expected[..]);
    proc.kill().unwrap();
}

#[test]
#[cfg(unix)]
fn test_domain_socket() {
    use std::io::prelude::*;
    use std::thread;
    use tempdir::TempDir;
    use unix_socket::UnixListener;

    let dir = TempDir::new("unix_socket").expect("failed to create dir");
    let socket_path = dir.path().join("sock");
    let listener = UnixListener::bind(&socket_path).expect("failed to create socket");

    let thread = thread::spawn(move || {
        let mut stream = listener.accept().expect("failed to accept connection").0;
        stream
            .write_all(b"a\tb")
            .expect("failed to write test data");
    });

    new_ucmd!()
        .args(&[socket_path])
        .succeeds()
        .stdout_only("a\tb");

    thread.join().unwrap();
}
<|endoftext|>


<commit_before>///! Allocating pages of memory from the OS and carving them into individual allocations.

use std::{cmp, mem, ptr};
use std::marker::PhantomData;
use bit_vec::BitVec;
use traits::IntoHeap;
use heap::Heap;

/// Non-inlined function that serves as an entry point to marking. This is used
/// for marking root set entries.
unsafe fn mark_entry_point<'h, T: IntoHeap<'h>>(addr: *const ()) {
    T::mark(&*(addr as *const T::In));
}

pub fn heap_type_id<'h, T: IntoHeap<'h>>() -> usize {
    mark_entry_point::<T> as *const () as usize
}

const PAGE_SIZE: usize = 0x1000;

/// We rely on all bits to the right of this bit being 0 in addresses of
/// TypedPage instances.
const PAGE_ALIGN: usize = 0x1000;

fn is_aligned(ptr: *const ()) -> bool {
    ptr as usize & (PAGE_ALIGN - 1) == 0
}

pub struct PageHeader {
    pub heap: *mut Heap,
    mark_bits: BitVec,
    allocated_bits: BitVec,
    mark_fn: unsafe fn(*const ()),
    sweep_fn: unsafe fn(&mut PageHeader),
    freelist: *mut ()
}

impl PageHeader {
    pub fn find(ptr: *mut ()) -> *mut PageHeader {
        let header_addr = ptr as usize & !(PAGE_ALIGN - 1);
        header_addr as *mut PageHeader
    }

    pub fn clear_mark_bits(&mut self) {
        self.mark_bits.clear();
    }

    /// True if nothing on this page is allocated.
    pub fn is_empty(&self) -> bool {
        self.allocated_bits.none()
    }

    pub unsafe fn mark(&self, ptr: *const ()) {
        (self.mark_fn)(ptr);
    }

    pub unsafe fn sweep(&mut self) {
        (self.sweep_fn)(self);
    }

    pub fn type_id(&self) -> usize {
        self.mark_fn as usize
    }

    pub fn downcast_mut<'h, T: IntoHeap<'h>>(&mut self) -> Option<&mut TypedPage<T::In>> {
        if heap_type_id::<T>() == self.type_id() {
            let ptr = self as *mut PageHeader as *mut TypedPage<T::In>;
            Some(unsafe { &mut *ptr })
        } else {
            None
        }
    }
}

pub struct TypedPage<U> {
    pub header: PageHeader,
    pub allocations: PhantomData<U>
}

/// Returns the smallest multiple of `k` that is at least `n`.
///
/// Panics if the answer is too big to fit in a `usize`.
fn round_up(n: usize, k: usize) -> usize {
    let a = n / k * k;
    if a == n { n } else { n + k }
}

impl<U> TypedPage<U> {
    /// The actual size of an allocation can't be smaller than the size of a
    /// pointer, due to the way we store the freelist by stealing a pointer
    /// from the allocation itself.
    fn allocation_size() -> usize {
        cmp::max(mem::size_of::<U>(), mem::size_of::<*mut U>())
    }

    fn allocation_align() -> usize {
        cmp::max(mem::align_of::<U>(), mem::align_of::<*mut U>())
    }

    /// Offset, in bytes, of the first allocation from the start of the page.
    fn first_allocation_offset() -> usize {
        round_up(mem::size_of::<PageHeader>(), Self::allocation_align())
    }

    /// Number of allocations that fit in a page.
    pub fn capacity() -> usize {
        (PAGE_SIZE - Self::first_allocation_offset()) / Self::allocation_size()
    }

    fn first_object_addr(&self) -> usize {
        (self as *const Self as usize) + Self::first_allocation_offset()
    }

    unsafe fn init(&mut self) {
        let mut addr = self.first_object_addr();
        for _ in 0 .. Self::capacity() {
            self.add_to_free_list(addr as *mut U);

            // This can't use `ptr = ptr.offset(1)` because if U is smaller
            // than a pointer, allocations are padded to pointer size.
            // `.offset(1)` doesn't know about the padding and therefore
            // wouldn't advance to the next allocation.
            addr += Self::allocation_size();
        }
    }

    /// Return the page containing the object `ptr` points to.
    pub fn find(ptr: *const U) -> *mut TypedPage<U> {
        let page_addr = ptr as usize & !(PAGE_ALIGN - 1);
        page_addr as *mut TypedPage<U>
    }

    unsafe fn add_to_free_list(&mut self, p: *mut U) {
        let listp = p as *mut *mut ();
        *listp = self.header.freelist;
        assert_eq!(*listp, self.header.freelist);
        self.header.freelist = p as *mut ();
    }

    unsafe fn allocation_index(&self, ptr: *const U) -> usize {
        let base = self.first_object_addr();

        // Check that ptr is in range.
        assert!(ptr >= base as *const U);
        assert!(ptr < (base + (Self::capacity() * Self::allocation_size())) as *const U);

        let index = (ptr as usize - base as usize) / Self::allocation_size();
        assert_eq!((base + index * Self::allocation_size()) as *const U, ptr);
        index
    }

    pub unsafe fn get_mark_bit(&mut self, ptr: *const U) -> bool {
        let index = self.allocation_index(ptr);
        self.header.mark_bits[index]
    }

    pub unsafe fn set_mark_bit(&mut self, ptr: *const U) {
        let index = self.allocation_index(ptr);
        self.header.mark_bits.set(index, true);
    }

    pub unsafe fn try_alloc(&mut self) -> Option<*mut U> {
        let p = self.header.freelist;
        if p.is_null() {
            None
        } else {
            let listp = p as *mut *mut ();
            self.header.freelist = *listp;
            let ap = p as *mut U;
            let index = self.allocation_index(ap);
            assert!(!self.header.allocated_bits[index]);
            self.header.allocated_bits.set(index, true);
            Some(ap)
        }
    }

    unsafe fn sweep(&mut self) {
        let mut addr = self.first_object_addr();
        for i in 0 .. Self::capacity() {
            if self.header.allocated_bits[i] && !self.header.mark_bits[i] {
                ptr::drop_in_place(addr as *mut U);
                self.header.allocated_bits.set(i, false);
                self.add_to_free_list(addr as *mut U);
            }
            addr += Self::allocation_size();
        }
    }
}

unsafe fn sweep_entry_point<'h, T: IntoHeap<'h>>(header: &mut PageHeader) {
    header.downcast_mut::<T>().unwrap().sweep();
}

pub struct PageBox(*mut PageHeader);

impl PageBox {
    pub fn new<'h, T: IntoHeap<'h>>(heap: *mut Heap) -> PageBox {
        assert!(mem::size_of::<TypedPage<T::In>>() <=
                TypedPage::<T::In>::first_allocation_offset());
        assert!(TypedPage::<T::In>::first_allocation_offset()
                + TypedPage::<T::In>::capacity() * TypedPage::<T::In>::allocation_size()
                <= PAGE_SIZE);
        let raw_page: *mut () = {
            let mut vec: Vec<u8> = Vec::with_capacity(PAGE_SIZE);
            let page = vec.as_mut_ptr() as *mut ();

            // Rust makes no guarantee whatsoever that this will work.
            // If it doesn't, panic.
            assert!(is_aligned(page));

            mem::forget(vec);
            page
        };

        let page: *mut TypedPage<T::In> = raw_page as *mut TypedPage<T::In>;
        let capacity = TypedPage::<T::In>::capacity();
        unsafe {
            ptr::write(page, TypedPage {
                header: PageHeader {
                    heap: heap,
                    mark_bits: BitVec::from_elem(capacity, false),
                    allocated_bits: BitVec::from_elem(capacity, false),
                    mark_fn: mark_entry_point::<T>,
                    sweep_fn: sweep_entry_point::<T>,
                    freelist: ptr::null_mut()
                },
                allocations: PhantomData
            });
            TypedPage::init(&mut *page);
            PageBox(&mut (*page).header as *mut PageHeader)
        }
    }
}

impl ::std::ops::Deref for PageBox {
    type Target = PageHeader;
    fn deref(&self) -> &PageHeader {
        unsafe { &*self.0 }
    }
}

impl ::std::ops::DerefMut for PageBox {
    fn deref_mut(&mut self) -> &mut PageHeader {
        unsafe { &mut *self.0 }
    }
}

impl Drop for PageBox {
    fn drop(&mut self) {
        unsafe {
            assert!((*self.0).is_empty());

            ptr::drop_in_place(self.0);

            // Deallocate the memory.
            Vec::from_raw_parts(self.0 as *mut u8, 0, PAGE_SIZE);
        }
    }
}
<commit_msg>Fix `pages` mod's doc comment<commit_after>//! Allocating pages of memory from the OS and carving them into individual
//! allocations.

use std::{cmp, mem, ptr};
use std::marker::PhantomData;
use bit_vec::BitVec;
use traits::IntoHeap;
use heap::Heap;

/// Non-inlined function that serves as an entry point to marking. This is used
/// for marking root set entries.
unsafe fn mark_entry_point<'h, T: IntoHeap<'h>>(addr: *const ()) {
    T::mark(&*(addr as *const T::In));
}

pub fn heap_type_id<'h, T: IntoHeap<'h>>() -> usize {
    mark_entry_point::<T> as *const () as usize
}

const PAGE_SIZE: usize = 0x1000;

/// We rely on all bits to the right of this bit being 0 in addresses of
/// TypedPage instances.
const PAGE_ALIGN: usize = 0x1000;

fn is_aligned(ptr: *const ()) -> bool {
    ptr as usize & (PAGE_ALIGN - 1) == 0
}

pub struct PageHeader {
    pub heap: *mut Heap,
    mark_bits: BitVec,
    allocated_bits: BitVec,
    mark_fn: unsafe fn(*const ()),
    sweep_fn: unsafe fn(&mut PageHeader),
    freelist: *mut ()
}

impl PageHeader {
    pub fn find(ptr: *mut ()) -> *mut PageHeader {
        let header_addr = ptr as usize & !(PAGE_ALIGN - 1);
        header_addr as *mut PageHeader
    }

    pub fn clear_mark_bits(&mut self) {
        self.mark_bits.clear();
    }

    /// True if nothing on this page is allocated.
    pub fn is_empty(&self) -> bool {
        self.allocated_bits.none()
    }

    pub unsafe fn mark(&self, ptr: *const ()) {
        (self.mark_fn)(ptr);
    }

    pub unsafe fn sweep(&mut self) {
        (self.sweep_fn)(self);
    }

    pub fn type_id(&self) -> usize {
        self.mark_fn as usize
    }

    pub fn downcast_mut<'h, T: IntoHeap<'h>>(&mut self) -> Option<&mut TypedPage<T::In>> {
        if heap_type_id::<T>() == self.type_id() {
            let ptr = self as *mut PageHeader as *mut TypedPage<T::In>;
            Some(unsafe { &mut *ptr })
        } else {
            None
        }
    }
}

pub struct TypedPage<U> {
    pub header: PageHeader,
    pub allocations: PhantomData<U>
}

/// Returns the smallest multiple of `k` that is at least `n`.
///
/// Panics if the answer is too big to fit in a `usize`.
fn round_up(n: usize, k: usize) -> usize {
    let a = n / k * k;
    if a == n { n } else { n + k }
}

impl<U> TypedPage<U> {
    /// The actual size of an allocation can't be smaller than the size of a
    /// pointer, due to the way we store the freelist by stealing a pointer
    /// from the allocation itself.
    fn allocation_size() -> usize {
        cmp::max(mem::size_of::<U>(), mem::size_of::<*mut U>())
    }

    fn allocation_align() -> usize {
        cmp::max(mem::align_of::<U>(), mem::align_of::<*mut U>())
    }

    /// Offset, in bytes, of the first allocation from the start of the page.
    fn first_allocation_offset() -> usize {
        round_up(mem::size_of::<PageHeader>(), Self::allocation_align())
    }

    /// Number of allocations that fit in a page.
    pub fn capacity() -> usize {
        (PAGE_SIZE - Self::first_allocation_offset()) / Self::allocation_size()
    }

    fn first_object_addr(&self) -> usize {
        (self as *const Self as usize) + Self::first_allocation_offset()
    }

    unsafe fn init(&mut self) {
        let mut addr = self.first_object_addr();
        for _ in 0 .. Self::capacity() {
            self.add_to_free_list(addr as *mut U);

            // This can't use `ptr = ptr.offset(1)` because if U is smaller
            // than a pointer, allocations are padded to pointer size.
            // `.offset(1)` doesn't know about the padding and therefore
            // wouldn't advance to the next allocation.
            addr += Self::allocation_size();
        }
    }

    /// Return the page containing the object `ptr` points to.
    pub fn find(ptr: *const U) -> *mut TypedPage<U> {
        let page_addr = ptr as usize & !(PAGE_ALIGN - 1);
        page_addr as *mut TypedPage<U>
    }

    unsafe fn add_to_free_list(&mut self, p: *mut U) {
        let listp = p as *mut *mut ();
        *listp = self.header.freelist;
        assert_eq!(*listp, self.header.freelist);
        self.header.freelist = p as *mut ();
    }

    unsafe fn allocation_index(&self, ptr: *const U) -> usize {
        let base = self.first_object_addr();

        // Check that ptr is in range.
        assert!(ptr >= base as *const U);
        assert!(ptr < (base + (Self::capacity() * Self::allocation_size())) as *const U);

        let index = (ptr as usize - base as usize) / Self::allocation_size();
        assert_eq!((base + index * Self::allocation_size()) as *const U, ptr);
        index
    }

    pub unsafe fn get_mark_bit(&mut self, ptr: *const U) -> bool {
        let index = self.allocation_index(ptr);
        self.header.mark_bits[index]
    }

    pub unsafe fn set_mark_bit(&mut self, ptr: *const U) {
        let index = self.allocation_index(ptr);
        self.header.mark_bits.set(index, true);
    }

    pub unsafe fn try_alloc(&mut self) -> Option<*mut U> {
        let p = self.header.freelist;
        if p.is_null() {
            None
        } else {
            let listp = p as *mut *mut ();
            self.header.freelist = *listp;
            let ap = p as *mut U;
            let index = self.allocation_index(ap);
            assert!(!self.header.allocated_bits[index]);
            self.header.allocated_bits.set(index, true);
            Some(ap)
        }
    }

    unsafe fn sweep(&mut self) {
        let mut addr = self.first_object_addr();
        for i in 0 .. Self::capacity() {
            if self.header.allocated_bits[i] && !self.header.mark_bits[i] {
                ptr::drop_in_place(addr as *mut U);
                self.header.allocated_bits.set(i, false);
                self.add_to_free_list(addr as *mut U);
            }
            addr += Self::allocation_size();
        }
    }
}

unsafe fn sweep_entry_point<'h, T: IntoHeap<'h>>(header: &mut PageHeader) {
    header.downcast_mut::<T>().unwrap().sweep();
}

pub struct PageBox(*mut PageHeader);

impl PageBox {
    pub fn new<'h, T: IntoHeap<'h>>(heap: *mut Heap) -> PageBox {
        assert!(mem::size_of::<TypedPage<T::In>>() <=
                TypedPage::<T::In>::first_allocation_offset());
        assert!(TypedPage::<T::In>::first_allocation_offset()
                + TypedPage::<T::In>::capacity() * TypedPage::<T::In>::allocation_size()
                <= PAGE_SIZE);
        let raw_page: *mut () = {
            let mut vec: Vec<u8> = Vec::with_capacity(PAGE_SIZE);
            let page = vec.as_mut_ptr() as *mut ();

            // Rust makes no guarantee whatsoever that this will work.
            // If it doesn't, panic.
            assert!(is_aligned(page));

            mem::forget(vec);
            page
        };

        let page: *mut TypedPage<T::In> = raw_page as *mut TypedPage<T::In>;
        let capacity = TypedPage::<T::In>::capacity();
        unsafe {
            ptr::write(page, TypedPage {
                header: PageHeader {
                    heap: heap,
                    mark_bits: BitVec::from_elem(capacity, false),
                    allocated_bits: BitVec::from_elem(capacity, false),
                    mark_fn: mark_entry_point::<T>,
                    sweep_fn: sweep_entry_point::<T>,
                    freelist: ptr::null_mut()
                },
                allocations: PhantomData
            });
            TypedPage::init(&mut *page);
            PageBox(&mut (*page).header as *mut PageHeader)
        }
    }
}

impl ::std::ops::Deref for PageBox {
    type Target = PageHeader;
    fn deref(&self) -> &PageHeader {
        unsafe { &*self.0 }
    }
}

impl ::std::ops::DerefMut for PageBox {
    fn deref_mut(&mut self) -> &mut PageHeader {
        unsafe { &mut *self.0 }
    }
}

impl Drop for PageBox {
    fn drop(&mut self) {
        unsafe {
            assert!((*self.0).is_empty());

            ptr::drop_in_place(self.0);

            // Deallocate the memory.
            Vec::from_raw_parts(self.0 as *mut u8, 0, PAGE_SIZE);
        }
    }
}
<|endoftext|>


<commit_before>use Icon;
use backend::MagogContext;
use brush::Frame;
use cache;
use calx_alg::timing;
use calx_color::color;
use calx_grid::{Dir6, FovValue, HexFov};
use euclid::{Point2D, point2, Vector2D, vec2, Rect};
use render::{self, Layer};
use sprite::Sprite;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::rc::Rc;
use world::{self, FovStatus, Location, Query, TerrainQuery, World};

/// Useful general constant for cell dimension ops.
pub static PIXEL_UNIT: f32 = 16.0;

pub struct WorldView {
    pub cursor_loc: Option<Location>,
    pub show_cursor: bool,
    camera_loc: Location,
    screen_area: Rect<f32>,
    fov: Option<HashMap<Vector2D<i32>, Vec<Location>>>,

    /// Mostly used in mapedit
    pub highlight_offscreen_tiles: bool,
}

impl WorldView {
    pub fn new(camera_loc: Location, screen_area: Rect<f32>) -> WorldView {
        WorldView {
            cursor_loc: None,
            show_cursor: false,
            camera_loc: camera_loc,
            screen_area: screen_area,
            fov: None,
            highlight_offscreen_tiles: false,
        }
    }

    pub fn set_camera(&mut self, loc: Location) {
        if loc != self.camera_loc {
            self.camera_loc = loc;
            self.fov = None;
        }
    }

    /// Recompute the cached screen view if the cache has been invalidated.
    fn ensure_fov(&mut self, world: &World) {
        if self.fov.is_none() {
            // Chart area, center in origin, inflated by tile width in every direction to get the cells
            // partially on screen included.
            let center = (self.screen_area.origin + self.screen_area.size / 2.0 -
                              vec2(PIXEL_UNIT / 2.0, 0.0)).to_vector();
            let bounds = self.screen_area
                .translate(&-(self.screen_area.origin + center).to_vector())
                .inflate(PIXEL_UNIT * 2.0, PIXEL_UNIT * 2.0);

            self.fov = Some(screen_fov(world, self.camera_loc, bounds));
        }
    }

    pub fn draw<C: MagogContext>(&mut self, world: &World, context: &mut C) {
        self.ensure_fov(world);

        let center = (self.screen_area.origin + self.screen_area.size / 2.0 -
                          vec2(PIXEL_UNIT / 2.0, 0.0)).to_vector();
        let chart = self.fov.as_ref().unwrap();
        let mut sprites = Vec::new();
        let cursor_pos = view_to_chart(context.mouse_pos() - center);

        for (&chart_pos, origins) in chart.iter() {
            assert!(!origins.is_empty());

            let mut loc = origins[0] + chart_pos;

            let in_map_memory;

            // If the chart position is in live FOV, we want to show the deepest stack coordinate.
            // If it's not, we want to start looking for the map memory one from the top of the
            // stack.
            if get_fov(world, loc) == Some(FovStatus::Seen) {
                in_map_memory = false;
            } else {
                in_map_memory = true;
                // Only accept map memory from the base layer (top of origins stack)
                loc = origins[origins.len() - 1] + chart_pos;
                if get_fov(world, loc) != Some(FovStatus::Remembered) {
                    // Bail out if there's no memory
                    continue;
                }
            }

            let screen_pos = chart_to_view(chart_pos.to_point()) + center;

            // TODO: Set up dynamic lighting, shade sprites based on angle and local light.
            render::draw_terrain_sprites(world, loc, |layer, _angle, brush, frame_idx| {
                sprites.push(Sprite {
                    layer: layer,
                    offset: [screen_pos.x as i32, screen_pos.y as i32],
                    brush: if in_map_memory {
                        map_memory_colorize(brush.clone())
                    } else {
                        brush.clone()
                    },
                    frame_idx: frame_idx,
                })
            });

            if !in_map_memory {
                for &i in &world.entities_at(loc) {
                    if let Some(desc) = world.ecs().desc.get(i) {
                        let layer = if world.is_mob(i) {
                            Layer::Object
                        } else {
                            Layer::Items
                        };
                        let frame_idx = if world.is_bobbing(i) {
                            timing::cycle_anim(1.0 / 3.0, 2)
                        } else {
                            0
                        };
                        sprites.push(Sprite {
                            layer: layer,
                            offset: [screen_pos.x as i32, screen_pos.y as i32],
                            brush: cache::entity(desc.icon),
                            frame_idx: frame_idx,
                        });
                    }
                }
            }

            if self.highlight_offscreen_tiles {
                if let Some(loc) = Location::origin().v2_at(loc) {
                    if !world::on_screen(loc) {
                        if Dir6::iter().any(|d| world::on_screen(loc + d.to_v2())) {
                            sprites.push(Sprite {
                                layer: Layer::Effect,
                                offset: [screen_pos.x as i32, screen_pos.y as i32],
                                brush: cache::misc(Icon::Portal),
                                frame_idx: 0,
                            });
                        } else {
                            sprites.push(Sprite {
                                layer: Layer::Effect,
                                offset: [screen_pos.x as i32, screen_pos.y as i32],
                                brush: cache::misc(Icon::CursorBottom),
                                frame_idx: 0,
                            });
                        }
                    }
                }
            }
        }

        // Draw cursor.
        if let Some(origins) = chart.get(&cursor_pos.to_vector()) {
            let screen_pos = chart_to_view(cursor_pos) + center;
            let loc = origins[0] + cursor_pos.to_vector();
            self.cursor_loc = Some(loc);

            if self.show_cursor {
                // TODO: Need a LOT less verbose API to add stuff to the sprite set.
                sprites.push(Sprite {
                    layer: Layer::Decal,
                    offset: [screen_pos.x as i32, screen_pos.y as i32],
                    brush: cache::misc(Icon::CursorBottom),
                    frame_idx: 0,
                });
                sprites.push(Sprite {
                    layer: Layer::Effect,
                    offset: [screen_pos.x as i32, screen_pos.y as i32],
                    brush: cache::misc(Icon::CursorTop),
                    frame_idx: 0,
                });
            }
        } else {
            self.cursor_loc = None;
        }

        sprites.sort();

        for i in &sprites {
            i.draw(context)
        }

        fn get_fov(world: &World, loc: Location) -> Option<FovStatus> {
            if let Some(player) = world.player() {
                world.ecs().map_memory.get(player).map_or(
                    Some(FovStatus::Seen),
                    |fov| fov.status(loc),
                )
            } else {
                Some(FovStatus::Seen)
            }
        }

        fn map_memory_colorize(brush: Rc<Vec<Frame>>) -> Rc<Vec<Frame>> {
            // XXX: This is horribly wasteful memory churning.
            use std::ops::Deref;

            let mut ret: Vec<Frame> = brush.deref().clone();
            for frame in ret.iter_mut() {
                for splat in frame.iter_mut() {
                    splat.color = color::BLACK.into_array();
                    splat.back_color = color::BROWN.into_array();
                }
            }

            Rc::new(ret)
        }
    }
}

/// Transform from chart space (unit is one map cell) to view space (unit is
/// one pixel).
pub fn chart_to_view(chart_pos: Point2D<i32>) -> Point2D<f32> {
    point2(
        (chart_pos.x as f32 * PIXEL_UNIT - chart_pos.y as f32 * PIXEL_UNIT),
        (chart_pos.x as f32 * PIXEL_UNIT / 2.0 + chart_pos.y as f32 * PIXEL_UNIT / 2.0),
    )
}

/// Transform from view space (unit is one pixel) to chart space (unit is one
/// map cell).
pub fn view_to_chart(view_pos: Point2D<f32>) -> Point2D<i32> {
    let c = PIXEL_UNIT / 2.0;
    let column = ((view_pos.x + c) / (c * 2.0)).floor();
    let row = ((view_pos.y - column * c) / (c * 2.0)).floor();
    point2((column + row) as i32, row as i32)
}

#[derive(Clone)]
struct ScreenFov<'a> {
    w: &'a World,
    screen_area: Rect<f32>,
    origins: Vec<Location>,
}

impl<'a> PartialEq for ScreenFov<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.w as *const World == other.w as *const World &&
            self.screen_area == other.screen_area && self.origins == other.origins
    }
}

impl<'a> Eq for ScreenFov<'a> {}

impl<'a> FovValue for ScreenFov<'a> {
    fn advance(&self, offset: Vector2D<i32>) -> Option<Self> {
        if !self.screen_area.contains(&chart_to_view(offset.to_point())) {
            return None;
        }

        let loc = self.origins[0] + offset;

        let mut ret = self.clone();
        // Go through a portal if terrain on our side of the portal is a void cell.
        //
        // With non-void terrain on top of the portal, just show our side and stay on the current
        // frame as far as FOV is concerned.
        if let Some(dest) = self.w.visible_portal(loc) {
            if self.w.is_border_portal(loc) {
                // A border portal will just overwrite the current origin. Map memory will
                // transition seamlessly to show the things past the border portal
                ret.origins[0] = dest - offset;
            } else {
                // A hole portal will add a new layer to the origins stack,
                // we still need to be able to show the previous layer when
                // showing map memory.
                ret.origins.insert(0, dest - offset);
            }
        }

        Some(ret)
    }
}

/// Return the field of view chart for drawing a screen.
///
/// The stack of locations in the return value lists origins for coordinate frames that have been
/// passed through when traversing portals, in reverse order. The first value is the origin of the
/// coordinate space you probably want to show for that point.
pub fn screen_fov(
    w: &World,
    origin: Location,
    screen_area: Rect<f32>,
) -> HashMap<Vector2D<i32>, Vec<Location>> {
    let init = ScreenFov {
        w: w,
        screen_area: screen_area,
        origins: vec![origin],
    };

    HashMap::from_iter(HexFov::new(init).map(|(pos, a)| (pos, a.origins)))
}

#[cfg(test)]
mod test {
    // FIXME: Allow constructing World instances without resource dependencies to allow lightweight
    // unit tests.
/*
    use euclid::{Point2D, Vector2D, Rect, Size2D};
    use world::{Location, Portal, World, Terraform};
    use super::{screen_fov, view_to_chart};

    fn test_world() -> World {
        use world::terrain::{Form, Kind, Tile};
        use calx_resource::ResourceStore;
        use world::Brush;
        use content;

        Brush::insert_resource("dummy".to_string(), Brush::new(Vec::new()));
        Brush::insert_resource("player".to_string(), Brush::new(Vec::new()));

        Tile::insert_resource(0, Tile::new("dummy", Kind::Block, Form::Void));
        Tile::insert_resource(1, Tile::new("dummy", Kind::Ground, Form::Gate));
        Tile::insert_resource(2, Tile::new("dummy", Kind::Ground, Form::Floor));
        Tile::insert_resource(3, Tile::new("dummy", Kind::Ground, Form::Floor));

        let mut ret = World::new(1);

        ret.set_terrain(Location::new(10, 10, 0), 2);
        ret.set_terrain(Location::new(11, 11, 0), 2);
        ret.set_terrain(Location::new(9, 9, 0), 2);
        ret.set_terrain(Location::new(10, 11, 0), 2);
        ret.set_terrain(Location::new(9, 10, 0), 2);
        ret.set_terrain(Location::new(10, 9, 0), 2);

        // Void for the see-through portal.
        ret.set_terrain(Location::new(11, 10, 0), 0);
        ret.set_portal(Location::new(11, 10, 0),
                       Portal::new(Location::new(11, 10, 0), Location::new(31, 10, 0)));
        ret.set_terrain(Location::new(31, 10, 0), 3);

        ret
    }

    #[test]
    fn test_portaling_fov() {
        let world = test_world();
        let fov = screen_fov(&world,
                             Location::new(10, 10, 0),
                             Rect::new(Point2D::new(-48.0, -48.0), Size2D::new(96.0, 96.0)));
        assert_eq!(fov.get(&vec2(0, 0)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(0, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(1, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(-1, -1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(1, 0)),
                   Some(&vec![Location::new(30, 10, 0), Location::new(10, 10, 0)]));
    }

    #[test]
    fn test_corner_visibility() {
        let world = test_world();
        let screen_rect = Rect::new(Point2D::new(-200.0, -200.0), Size2D::new(400.0, 400.0));
        let fov = screen_fov(&world, Location::new(10, 10, 0), screen_rect);

        // Check that the fov is bounded close to the given rectangle.

        let inside_screen = screen_rect.inflate(-40.0, -40.0);
        assert!(fov.get(&view_to_chart(inside_screen.origin)).is_some());
        assert!(fov.get(&view_to_chart(inside_screen.bottom_left())).is_some());
        assert!(fov.get(&view_to_chart(inside_screen.top_right())).is_some());
        assert!(fov.get(&view_to_chart(inside_screen.bottom_right())).is_some());

        let outside_screen = screen_rect.inflate(40.0, 40.0);
        assert!(fov.get(&view_to_chart(outside_screen.origin)).is_none());
        assert!(fov.get(&view_to_chart(outside_screen.bottom_left())).is_none());
        assert!(fov.get(&view_to_chart(outside_screen.top_right())).is_none());
        assert!(fov.get(&view_to_chart(outside_screen.bottom_right())).is_none());
    }
*/

}
<commit_msg>Original map memory color<commit_after>use Icon;
use backend::MagogContext;
use brush::Frame;
use cache;
use calx_alg::timing;
use calx_color::{Rgba, color};
use calx_grid::{Dir6, FovValue, HexFov};
use euclid::{Point2D, point2, Vector2D, vec2, Rect};
use render::{self, Layer};
use sprite::Sprite;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::rc::Rc;
use world::{self, FovStatus, Location, Query, TerrainQuery, World};

/// Useful general constant for cell dimension ops.
pub static PIXEL_UNIT: f32 = 16.0;

pub struct WorldView {
    pub cursor_loc: Option<Location>,
    pub show_cursor: bool,
    camera_loc: Location,
    screen_area: Rect<f32>,
    fov: Option<HashMap<Vector2D<i32>, Vec<Location>>>,

    /// Mostly used in mapedit
    pub highlight_offscreen_tiles: bool,
}

impl WorldView {
    pub fn new(camera_loc: Location, screen_area: Rect<f32>) -> WorldView {
        WorldView {
            cursor_loc: None,
            show_cursor: false,
            camera_loc: camera_loc,
            screen_area: screen_area,
            fov: None,
            highlight_offscreen_tiles: false,
        }
    }

    pub fn set_camera(&mut self, loc: Location) {
        if loc != self.camera_loc {
            self.camera_loc = loc;
            self.fov = None;
        }
    }

    /// Recompute the cached screen view if the cache has been invalidated.
    fn ensure_fov(&mut self, world: &World) {
        if self.fov.is_none() {
            // Chart area, center in origin, inflated by tile width in every direction to get the cells
            // partially on screen included.
            let center = (self.screen_area.origin + self.screen_area.size / 2.0 -
                              vec2(PIXEL_UNIT / 2.0, 0.0)).to_vector();
            let bounds = self.screen_area
                .translate(&-(self.screen_area.origin + center).to_vector())
                .inflate(PIXEL_UNIT * 2.0, PIXEL_UNIT * 2.0);

            self.fov = Some(screen_fov(world, self.camera_loc, bounds));
        }
    }

    pub fn draw<C: MagogContext>(&mut self, world: &World, context: &mut C) {
        self.ensure_fov(world);

        let center = (self.screen_area.origin + self.screen_area.size / 2.0 -
                          vec2(PIXEL_UNIT / 2.0, 0.0)).to_vector();
        let chart = self.fov.as_ref().unwrap();
        let mut sprites = Vec::new();
        let cursor_pos = view_to_chart(context.mouse_pos() - center);

        for (&chart_pos, origins) in chart.iter() {
            assert!(!origins.is_empty());

            let mut loc = origins[0] + chart_pos;

            let in_map_memory;

            // If the chart position is in live FOV, we want to show the deepest stack coordinate.
            // If it's not, we want to start looking for the map memory one from the top of the
            // stack.
            if get_fov(world, loc) == Some(FovStatus::Seen) {
                in_map_memory = false;
            } else {
                in_map_memory = true;
                // Only accept map memory from the base layer (top of origins stack)
                loc = origins[origins.len() - 1] + chart_pos;
                if get_fov(world, loc) != Some(FovStatus::Remembered) {
                    // Bail out if there's no memory
                    continue;
                }
            }

            let screen_pos = chart_to_view(chart_pos.to_point()) + center;

            // TODO: Set up dynamic lighting, shade sprites based on angle and local light.
            render::draw_terrain_sprites(world, loc, |layer, _angle, brush, frame_idx| {
                sprites.push(Sprite {
                    layer: layer,
                    offset: [screen_pos.x as i32, screen_pos.y as i32],
                    brush: if in_map_memory {
                        map_memory_colorize(brush.clone())
                    } else {
                        brush.clone()
                    },
                    frame_idx: frame_idx,
                })
            });

            if !in_map_memory {
                for &i in &world.entities_at(loc) {
                    if let Some(desc) = world.ecs().desc.get(i) {
                        let layer = if world.is_mob(i) {
                            Layer::Object
                        } else {
                            Layer::Items
                        };
                        let frame_idx = if world.is_bobbing(i) {
                            timing::cycle_anim(1.0 / 3.0, 2)
                        } else {
                            0
                        };
                        sprites.push(Sprite {
                            layer: layer,
                            offset: [screen_pos.x as i32, screen_pos.y as i32],
                            brush: cache::entity(desc.icon),
                            frame_idx: frame_idx,
                        });
                    }
                }
            }

            if self.highlight_offscreen_tiles {
                if let Some(loc) = Location::origin().v2_at(loc) {
                    if !world::on_screen(loc) {
                        if Dir6::iter().any(|d| world::on_screen(loc + d.to_v2())) {
                            sprites.push(Sprite {
                                layer: Layer::Effect,
                                offset: [screen_pos.x as i32, screen_pos.y as i32],
                                brush: cache::misc(Icon::Portal),
                                frame_idx: 0,
                            });
                        } else {
                            sprites.push(Sprite {
                                layer: Layer::Effect,
                                offset: [screen_pos.x as i32, screen_pos.y as i32],
                                brush: cache::misc(Icon::CursorBottom),
                                frame_idx: 0,
                            });
                        }
                    }
                }
            }
        }

        // Draw cursor.
        if let Some(origins) = chart.get(&cursor_pos.to_vector()) {
            let screen_pos = chart_to_view(cursor_pos) + center;
            let loc = origins[0] + cursor_pos.to_vector();
            self.cursor_loc = Some(loc);

            if self.show_cursor {
                // TODO: Need a LOT less verbose API to add stuff to the sprite set.
                sprites.push(Sprite {
                    layer: Layer::Decal,
                    offset: [screen_pos.x as i32, screen_pos.y as i32],
                    brush: cache::misc(Icon::CursorBottom),
                    frame_idx: 0,
                });
                sprites.push(Sprite {
                    layer: Layer::Effect,
                    offset: [screen_pos.x as i32, screen_pos.y as i32],
                    brush: cache::misc(Icon::CursorTop),
                    frame_idx: 0,
                });
            }
        } else {
            self.cursor_loc = None;
        }

        sprites.sort();

        for i in &sprites {
            i.draw(context)
        }

        fn get_fov(world: &World, loc: Location) -> Option<FovStatus> {
            if let Some(player) = world.player() {
                world.ecs().map_memory.get(player).map_or(
                    Some(FovStatus::Seen),
                    |fov| fov.status(loc),
                )
            } else {
                Some(FovStatus::Seen)
            }
        }

        fn map_memory_colorize(brush: Rc<Vec<Frame>>) -> Rc<Vec<Frame>> {
            // XXX: This is horribly wasteful memory churning.
            use std::ops::Deref;

            let mut ret: Vec<Frame> = brush.deref().clone();
            for frame in ret.iter_mut() {
                for splat in frame.iter_mut() {
                    splat.color = color::BLACK.into_array();
                    splat.back_color = Rgba::from(0x332200ff).into_array();
                }
            }

            Rc::new(ret)
        }
    }
}

/// Transform from chart space (unit is one map cell) to view space (unit is
/// one pixel).
pub fn chart_to_view(chart_pos: Point2D<i32>) -> Point2D<f32> {
    point2(
        (chart_pos.x as f32 * PIXEL_UNIT - chart_pos.y as f32 * PIXEL_UNIT),
        (chart_pos.x as f32 * PIXEL_UNIT / 2.0 + chart_pos.y as f32 * PIXEL_UNIT / 2.0),
    )
}

/// Transform from view space (unit is one pixel) to chart space (unit is one
/// map cell).
pub fn view_to_chart(view_pos: Point2D<f32>) -> Point2D<i32> {
    let c = PIXEL_UNIT / 2.0;
    let column = ((view_pos.x + c) / (c * 2.0)).floor();
    let row = ((view_pos.y - column * c) / (c * 2.0)).floor();
    point2((column + row) as i32, row as i32)
}

#[derive(Clone)]
struct ScreenFov<'a> {
    w: &'a World,
    screen_area: Rect<f32>,
    origins: Vec<Location>,
}

impl<'a> PartialEq for ScreenFov<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.w as *const World == other.w as *const World &&
            self.screen_area == other.screen_area && self.origins == other.origins
    }
}

impl<'a> Eq for ScreenFov<'a> {}

impl<'a> FovValue for ScreenFov<'a> {
    fn advance(&self, offset: Vector2D<i32>) -> Option<Self> {
        if !self.screen_area.contains(&chart_to_view(offset.to_point())) {
            return None;
        }

        let loc = self.origins[0] + offset;

        let mut ret = self.clone();
        // Go through a portal if terrain on our side of the portal is a void cell.
        //
        // With non-void terrain on top of the portal, just show our side and stay on the current
        // frame as far as FOV is concerned.
        if let Some(dest) = self.w.visible_portal(loc) {
            if self.w.is_border_portal(loc) {
                // A border portal will just overwrite the current origin. Map memory will
                // transition seamlessly to show the things past the border portal
                ret.origins[0] = dest - offset;
            } else {
                // A hole portal will add a new layer to the origins stack,
                // we still need to be able to show the previous layer when
                // showing map memory.
                ret.origins.insert(0, dest - offset);
            }
        }

        Some(ret)
    }
}

/// Return the field of view chart for drawing a screen.
///
/// The stack of locations in the return value lists origins for coordinate frames that have been
/// passed through when traversing portals, in reverse order. The first value is the origin of the
/// coordinate space you probably want to show for that point.
pub fn screen_fov(
    w: &World,
    origin: Location,
    screen_area: Rect<f32>,
) -> HashMap<Vector2D<i32>, Vec<Location>> {
    let init = ScreenFov {
        w: w,
        screen_area: screen_area,
        origins: vec![origin],
    };

    HashMap::from_iter(HexFov::new(init).map(|(pos, a)| (pos, a.origins)))
}

#[cfg(test)]
mod test {
    // FIXME: Allow constructing World instances without resource dependencies to allow lightweight
    // unit tests.
/*
    use euclid::{Point2D, Vector2D, Rect, Size2D};
    use world::{Location, Portal, World, Terraform};
    use super::{screen_fov, view_to_chart};

    fn test_world() -> World {
        use world::terrain::{Form, Kind, Tile};
        use calx_resource::ResourceStore;
        use world::Brush;
        use content;

        Brush::insert_resource("dummy".to_string(), Brush::new(Vec::new()));
        Brush::insert_resource("player".to_string(), Brush::new(Vec::new()));

        Tile::insert_resource(0, Tile::new("dummy", Kind::Block, Form::Void));
        Tile::insert_resource(1, Tile::new("dummy", Kind::Ground, Form::Gate));
        Tile::insert_resource(2, Tile::new("dummy", Kind::Ground, Form::Floor));
        Tile::insert_resource(3, Tile::new("dummy", Kind::Ground, Form::Floor));

        let mut ret = World::new(1);

        ret.set_terrain(Location::new(10, 10, 0), 2);
        ret.set_terrain(Location::new(11, 11, 0), 2);
        ret.set_terrain(Location::new(9, 9, 0), 2);
        ret.set_terrain(Location::new(10, 11, 0), 2);
        ret.set_terrain(Location::new(9, 10, 0), 2);
        ret.set_terrain(Location::new(10, 9, 0), 2);

        // Void for the see-through portal.
        ret.set_terrain(Location::new(11, 10, 0), 0);
        ret.set_portal(Location::new(11, 10, 0),
                       Portal::new(Location::new(11, 10, 0), Location::new(31, 10, 0)));
        ret.set_terrain(Location::new(31, 10, 0), 3);

        ret
    }

    #[test]
    fn test_portaling_fov() {
        let world = test_world();
        let fov = screen_fov(&world,
                             Location::new(10, 10, 0),
                             Rect::new(Point2D::new(-48.0, -48.0), Size2D::new(96.0, 96.0)));
        assert_eq!(fov.get(&vec2(0, 0)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(0, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(1, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(-1, -1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&vec2(1, 0)),
                   Some(&vec![Location::new(30, 10, 0), Location::new(10, 10, 0)]));
    }

    #[test]
    fn test_corner_visibility() {
        let world = test_world();
        let screen_rect = Rect::new(Point2D::new(-200.0, -200.0), Size2D::new(400.0, 400.0));
        let fov = screen_fov(&world, Location::new(10, 10, 0), screen_rect);

        // Check that the fov is bounded close to the given rectangle.

        let inside_screen = screen_rect.inflate(-40.0, -40.0);
        assert!(fov.get(&view_to_chart(inside_screen.origin)).is_some());
        assert!(fov.get(&view_to_chart(inside_screen.bottom_left())).is_some());
        assert!(fov.get(&view_to_chart(inside_screen.top_right())).is_some());
        assert!(fov.get(&view_to_chart(inside_screen.bottom_right())).is_some());

        let outside_screen = screen_rect.inflate(40.0, 40.0);
        assert!(fov.get(&view_to_chart(outside_screen.origin)).is_none());
        assert!(fov.get(&view_to_chart(outside_screen.bottom_left())).is_none());
        assert!(fov.get(&view_to_chart(outside_screen.top_right())).is_none());
        assert!(fov.get(&view_to_chart(outside_screen.bottom_right())).is_none());
    }
*/

}
<|endoftext|>


<commit_before>use bytes::{BufMut, BytesMut};

use nom::{IResult, Needed};

use imap_proto;
use imap_proto::types::{Request, RequestId, Response};

use std::io;
use std::mem;

use tokio::net::TcpStream;
use tokio_io::codec::{Decoder, Encoder, Framed};
use tokio_tls::TlsStream;

pub type ImapTransport = Framed<TlsStream<TcpStream>, ImapCodec>;

pub struct ImapCodec {
    decode_need_message_bytes: usize,
}

impl Default for ImapCodec {
    fn default() -> Self {
        Self { decode_need_message_bytes: 0 }
    }
}

impl<'a> Decoder for ImapCodec {
    type Item = ResponseData;
    type Error = io::Error;
    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, io::Error> {
        if self.decode_need_message_bytes > buf.len() {
            return Ok(None);
        }
        let res = match imap_proto::parse_response(buf) {
            IResult::Done(remaining, response) => {
                // This SHOULD be acceptable/safe: BytesMut storage memory is
                // allocated on the heap and should not move. It will not be
                // freed as long as we keep a reference alive, which we do
                // by retaining a reference to the split buffer, below.
                let response = unsafe { mem::transmute(response) };
                Some((response, buf.len() - remaining.len()))
            },
            IResult::Incomplete(Needed::Size(min)) => {
                self.decode_need_message_bytes = min;
                return Ok(None);
            },
            IResult::Incomplete(_) => {
                return Ok(None);
            },
            IResult::Error(err) => panic!("error {} during parsing of {:?}", err, buf),
        };
        let (response, rsp_len) = res.unwrap();
        let raw = buf.split_to(rsp_len);
        self.decode_need_message_bytes = 0;
        Ok(Some(ResponseData { raw, response }))
    }
}

impl Encoder for ImapCodec {
    type Item = Request;
    type Error = io::Error;
    fn encode(&mut self, msg: Self::Item, dst: &mut BytesMut) -> Result<(), io::Error> {
        dst.put(msg.0.as_bytes());
        dst.put(b' ');
        dst.put(&msg.1);
        dst.put("\r\n");
        Ok(())
    }
}

#[derive(Debug)]
pub struct ResponseData {
    raw: BytesMut,
    // This reference is really scoped to the lifetime of the `raw`
    // member, but unfortunately Rust does not allow that yet. It
    // is transmuted to `'static` by the `Decoder`, instead, and
    // references returned to callers of `ResponseData` are limited
    // to the lifetime of the `ResponseData` struct.
    pub response: Response<'static>,
}

impl ResponseData {
    pub fn request_id(&self) -> Option<&RequestId> {
        match self.response {
            Response::Done { ref tag, .. } => Some(tag),
            _ => None,
        }
    }
    pub fn parsed(&self) -> &Response {
        unsafe { mem::transmute(&self.response) }
    }
}
<commit_msg>No need to wrap parser results in Option<commit_after>use bytes::{BufMut, BytesMut};

use nom::{IResult, Needed};

use imap_proto;
use imap_proto::types::{Request, RequestId, Response};

use std::io;
use std::mem;

use tokio::net::TcpStream;
use tokio_io::codec::{Decoder, Encoder, Framed};
use tokio_tls::TlsStream;

pub type ImapTransport = Framed<TlsStream<TcpStream>, ImapCodec>;

pub struct ImapCodec {
    decode_need_message_bytes: usize,
}

impl Default for ImapCodec {
    fn default() -> Self {
        Self { decode_need_message_bytes: 0 }
    }
}

impl<'a> Decoder for ImapCodec {
    type Item = ResponseData;
    type Error = io::Error;
    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, io::Error> {
        if self.decode_need_message_bytes > buf.len() {
            return Ok(None);
        }
        let (response, rsp_len) = match imap_proto::parse_response(buf) {
            IResult::Done(remaining, response) => {
                // This SHOULD be acceptable/safe: BytesMut storage memory is
                // allocated on the heap and should not move. It will not be
                // freed as long as we keep a reference alive, which we do
                // by retaining a reference to the split buffer, below.
                let response = unsafe { mem::transmute(response) };
                (response, buf.len() - remaining.len())
            },
            IResult::Incomplete(Needed::Size(min)) => {
                self.decode_need_message_bytes = min;
                return Ok(None);
            },
            IResult::Incomplete(_) => {
                return Ok(None);
            },
            IResult::Error(err) => panic!("error {} during parsing of {:?}", err, buf),
        };
        let raw = buf.split_to(rsp_len);
        self.decode_need_message_bytes = 0;
        Ok(Some(ResponseData { raw, response }))
    }
}

impl Encoder for ImapCodec {
    type Item = Request;
    type Error = io::Error;
    fn encode(&mut self, msg: Self::Item, dst: &mut BytesMut) -> Result<(), io::Error> {
        dst.put(msg.0.as_bytes());
        dst.put(b' ');
        dst.put(&msg.1);
        dst.put("\r\n");
        Ok(())
    }
}

#[derive(Debug)]
pub struct ResponseData {
    raw: BytesMut,
    // This reference is really scoped to the lifetime of the `raw`
    // member, but unfortunately Rust does not allow that yet. It
    // is transmuted to `'static` by the `Decoder`, instead, and
    // references returned to callers of `ResponseData` are limited
    // to the lifetime of the `ResponseData` struct.
    pub response: Response<'static>,
}

impl ResponseData {
    pub fn request_id(&self) -> Option<&RequestId> {
        match self.response {
            Response::Done { ref tag, .. } => Some(tag),
            _ => None,
        }
    }
    pub fn parsed(&self) -> &Response {
        unsafe { mem::transmute(&self.response) }
    }
}
<|endoftext|>


<commit_before>pub fn range(lo: uint, hi: uint, it: &fn(uint)) {
    let mut iter = lo;
    while iter < hi {
        it(iter);
        iter += 1;
    }
}

#[inline]
pub fn to_str_bytes(num: int, radix: int, f: &fn(u8)) {
	let neg = num < 0;
	let mut deccum = num;

    // Radix can be as low as 2, so we need 64 characters for a number
    // near 2^64, plus another one for a possible '-' character.
    let mut buf = [0u8, ..65];
    let mut cur = 0;

    loop {
        let digit_signed = deccum % radix;
        let digit = if digit_signed < 0 { -digit_signed } else { digit_signed };
        buf[cur] = match digit as u8 {
            i @ 0..9 => '0' as u8 + i,
            i        => 'a' as u8 + (i-10),
        };
        cur += 1;
        deccum /= radix as int;
        if deccum == 0 { break; }
    }

    if neg {
        f('-' as u8);
    }

    while cur > 0 {
        cur -= 1;
        f(buf[cur]);
    }
}
<commit_msg>Refactor int::to_str_bytes<commit_after>pub fn range(lo: uint, hi: uint, it: &fn(uint)) {
    let mut iter = lo;
    while iter < hi {
        it(iter);
        iter += 1;
    }
}

#[inline]
pub fn to_str_bytes(num: int, radix: int, f: &fn(u8)) {
	let neg = num < 0;
	let mut deccum = if neg { -num } else { num };

    // Radix can be as low as 2, so we need 64 characters for a number
    // near 2^64, plus another one for a possible '-' character.
    let mut buf = [0u8, ..65];
    let mut cur = 0;

    loop {
        let digit = deccum % radix;
        buf[cur] = match digit as u8 {
            i @ 0..9 => '0' as u8 + i,
            i        => 'a' as u8 + (i-10),
        };
        cur += 1;
        deccum /= radix as int;
        if deccum == 0 { break; }
    }

    if neg {
        f('-' as u8);
    }

    while cur > 0 {
        cur -= 1;
        f(buf[cur]);
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;
use session_types::*;
use peano::{Peano,Pop};
use super::IO;

/// A `Protocol` describes the underlying protocol, including the "initial" session
/// type. `Handler`s are defined over concrete `Protocol`s to implement the behavior
/// of a protocol in a given `SessionType`.
pub trait Protocol {
    type Initial: SessionType;
}

/// `Session`s are containers of channels which store the current session type
/// internally through a function pointer to a concrete handler.
pub struct Session<P: Protocol, I>(Defer<P, I>);

/// Handlers must return `Defer` to indicate to the `Session` how to proceed in
/// the future. `Defer` can be obtained by calling `.defer()` on the channel, or
/// by calling `.close()` when the session is `Eps`.
pub struct Defer<P: Protocol, I>(pub DeferFunc<I, P, (), ()>, pub PhantomData<P>, pub bool);

#[doc(hidden)]
pub type DeferFunc<I, P, E, S> = for<'a> fn(Chan<'a, I, E, S>) -> Defer<P, I>;

/// Channels are provided to handlers to act as a "courier" for the session type
/// and a guard for the IO backend.
pub struct Chan<'a, I: 'a, E: SessionType, S: SessionType>(&'a mut I, PhantomData<(E, S)>);

/// `Handler` is implemented on `Protocol` for every session type you expect to defer,
/// including the initial state.
pub trait Handler<I, E: SessionType, S: SessionType>: Protocol + Sized {
    /// Given a channel in a particular state, with a particular environment,
    /// do whatever you'd like with the channel and return `Defer`, which you
    /// can obtain by doing `.defer()` on the channel or `.close()` on the
    /// channel.
    fn with<'a>(Chan<'a, I, E, S>) -> Defer<Self, I>;
}

impl<I, P: Handler<I, (), <P as Protocol>::Initial>> Session<P, I> {
    /// Create a new session initialized to the Protocol.
    pub fn new() -> Session<P, I> {
        let starting_func: DeferFunc<I, P, (), P::Initial> = Handler::<I, (), P::Initial>::with;

        Session(Defer(unsafe { mem::transmute(starting_func) }, PhantomData, true))
    }
}

impl<I, P: Handler<I, (), <<P as Protocol>::Initial as SessionType>::Dual>> Session<P, I> {
    /// Create a new session initialized to the dual of the Protocol.
    pub fn new_dual() -> Session<P, I> {
        let starting_func: DeferFunc<I, P, (), <P::Initial as SessionType>::Dual> = Handler::<I, (), <P::Initial as SessionType>::Dual>::with;

        Session(Defer(unsafe { mem::transmute(starting_func) }, PhantomData, true))
    }
}

impl<'a, P: Protocol, I> Session<P, I> {
    /// Operates the handler, returning false if the channel closed. This will panic
    /// if the channel was closed in a previous call.
    pub fn with(&mut self, io: &'a mut I) -> bool {
        // Construct a channel with a blank environment and session type.
        // These will be considered different, concrete types by the handler
        // we call.
        let p: Chan<'a, I, (), ()> = Chan(io, PhantomData);

        let new = ((self.0).0)(p);
        self.0 = new;

        return (self.0).2;
    }
}

impl<'a, I, E: SessionType, S: SessionType> Chan<'a, I, E, S> {
    /// Defer the rest of the protocol execution. Useful for returning early.
    ///
    /// There must be a [`Handler`](trait.Handler.html) implemented for the protocol state you're deferring.
    pub fn defer<P: Handler<I, E, S>>(self) -> Defer<P, I> {
        let next_func: DeferFunc<I, P, E, S> = Handler::<I, E, S>::with;

        Defer(unsafe { mem::transmute(next_func) }, PhantomData, true)
    }
}

// TODO: refactor IO, add supertrait for close()
impl<'a, I: IO<usize>, E: SessionType> Chan<'a, I, E, Eps> {
    /// Close the channel. Only possible if it's in `Eps` (epsilon) state.
    pub fn close<P: Protocol>(self) -> Defer<P, I> {
        self.0.close();

        let next_func: DeferFunc<I, P, E, Eps> = Dummy::<I, P, E, Eps>::with;

        Defer(unsafe { mem::transmute(next_func) }, PhantomData, false)
    }
}

impl<'a, I: IO<A>, A, E: SessionType, S: SessionType> Chan<'a, I, E, Send<A, S>> {
    /// Send an `A` to IO.
    pub fn send(self, a: A) -> Chan<'a, I, E, S> {
        self.0.send(a);

        unsafe { mem::transmute(self) }
    }
}

impl<'a, I: IO<A>, A, E: SessionType, S: SessionType> Chan<'a, I, E, Recv<A, S>> {
    /// Receive an `A` from IO.
    pub fn recv(self) -> Result<(A, Chan<'a, I, E, S>), Self> {
        match self.0.recv() {
            Some(res) => Ok((res, unsafe { mem::transmute(self) })),
            None => {
                Err(self)
            }
        }
    }
}

impl<'a, I, E: SessionType, S: SessionType> Chan<'a, I, E, Nest<S>> {
    /// Enter into a nested protocol.
    pub fn enter(self) -> Chan<'a, I, (S, E), S> {
        unsafe { mem::transmute(self) }
    }
}

impl<'a, I, N: Peano, E: SessionType + Pop<N>> Chan<'a, I, E, Escape<N>> {
    /// Escape from a nested protocol.
    pub fn pop(self) -> Chan<'a, I, E::Tail, E::Head> {
        Chan(self.0, PhantomData)
    }
}

impl<'a, I: IO<u8>, E: SessionType, P: SessionType> Chan<'a, I, E, P> {
    /// Select a protocol to advance to.
    pub fn choose<S: SessionType>(self) -> Chan<'a, I, E, S> where P: Chooser<S> {
        self.0.send(P::num());

        Chan(self.0, PhantomData)
    }
}

impl<'a, I: IO<u8>, E: SessionType, S: SessionType, Q: SessionType> Chan<'a, I, E, Accept<S, Q>> {
    /// Accept one of many protocols and advance to its handler.
    pub fn accept<P: Protocol + Handler<I, E, Accept<S, Q>> + Acceptor<I, E, Accept<S, Q>>>(self) -> Defer<P, I> {
        match self.0.recv() {
            Some(num) => {
                <P as Acceptor<I, E, Accept<S, Q>>>::defer(num)
            },
            None => {
                self.defer()
            }
        }
    }
}

struct Dummy<I, P, E, S>(PhantomData<(I, P, E, S)>);
impl<I, P: Protocol, E: SessionType, S: SessionType> Dummy<I, P, E, S> {
    fn with<'a>(_: Chan<'a, I, E, S>) -> Defer<P, I> {
        panic!("Channel was closed!");
    }
}<commit_msg>Remove some unnecessary transmutes<commit_after>use std::marker::PhantomData;
use std::mem;
use session_types::*;
use peano::{Peano,Pop};
use super::IO;

/// A `Protocol` describes the underlying protocol, including the "initial" session
/// type. `Handler`s are defined over concrete `Protocol`s to implement the behavior
/// of a protocol in a given `SessionType`.
pub trait Protocol {
    type Initial: SessionType;
}

/// `Session`s are containers of channels which store the current session type
/// internally through a function pointer to a concrete handler.
pub struct Session<P: Protocol, I>(Defer<P, I>);

/// Handlers must return `Defer` to indicate to the `Session` how to proceed in
/// the future. `Defer` can be obtained by calling `.defer()` on the channel, or
/// by calling `.close()` when the session is `Eps`.
pub struct Defer<P: Protocol, I>(pub DeferFunc<I, P, (), ()>, pub PhantomData<P>, pub bool);

#[doc(hidden)]
pub type DeferFunc<I, P, E, S> = for<'a> fn(Chan<'a, I, E, S>) -> Defer<P, I>;

/// Channels are provided to handlers to act as a "courier" for the session type
/// and a guard for the IO backend.
pub struct Chan<'a, I: 'a, E: SessionType, S: SessionType>(&'a mut I, PhantomData<(E, S)>);

/// `Handler` is implemented on `Protocol` for every session type you expect to defer,
/// including the initial state.
pub trait Handler<I, E: SessionType, S: SessionType>: Protocol + Sized {
    /// Given a channel in a particular state, with a particular environment,
    /// do whatever you'd like with the channel and return `Defer`, which you
    /// can obtain by doing `.defer()` on the channel or `.close()` on the
    /// channel.
    fn with<'a>(Chan<'a, I, E, S>) -> Defer<Self, I>;
}

impl<I, P: Handler<I, (), <P as Protocol>::Initial>> Session<P, I> {
    /// Create a new session initialized to the Protocol.
    pub fn new() -> Session<P, I> {
        let starting_func: DeferFunc<I, P, (), P::Initial> = Handler::<I, (), P::Initial>::with;

        Session(Defer(unsafe { mem::transmute(starting_func) }, PhantomData, true))
    }
}

impl<I, P: Handler<I, (), <<P as Protocol>::Initial as SessionType>::Dual>> Session<P, I> {
    /// Create a new session initialized to the dual of the Protocol.
    pub fn new_dual() -> Session<P, I> {
        let starting_func: DeferFunc<I, P, (), <P::Initial as SessionType>::Dual> = Handler::<I, (), <P::Initial as SessionType>::Dual>::with;

        Session(Defer(unsafe { mem::transmute(starting_func) }, PhantomData, true))
    }
}

impl<'a, P: Protocol, I> Session<P, I> {
    /// Operates the handler, returning false if the channel closed. This will panic
    /// if the channel was closed in a previous call.
    pub fn with(&mut self, io: &'a mut I) -> bool {
        // Construct a channel with a blank environment and session type.
        // These will be considered different, concrete types by the handler
        // we call.
        let p: Chan<'a, I, (), ()> = Chan(io, PhantomData);

        let new = ((self.0).0)(p);
        self.0 = new;

        return (self.0).2;
    }
}

impl<'a, I, E: SessionType, S: SessionType> Chan<'a, I, E, S> {
    /// Defer the rest of the protocol execution. Useful for returning early.
    ///
    /// There must be a [`Handler`](trait.Handler.html) implemented for the protocol state you're deferring.
    pub fn defer<P: Handler<I, E, S>>(self) -> Defer<P, I> {
        let next_func: DeferFunc<I, P, E, S> = Handler::<I, E, S>::with;

        Defer(unsafe { mem::transmute(next_func) }, PhantomData, true)
    }
}

// TODO: refactor IO, add supertrait for close()
impl<'a, I: IO<usize>, E: SessionType> Chan<'a, I, E, Eps> {
    /// Close the channel. Only possible if it's in `Eps` (epsilon) state.
    pub fn close<P: Protocol>(self) -> Defer<P, I> {
        self.0.close();

        let next_func: DeferFunc<I, P, E, Eps> = Dummy::<I, P, E, Eps>::with;

        Defer(unsafe { mem::transmute(next_func) }, PhantomData, false)
    }
}

impl<'a, I: IO<A>, A, E: SessionType, S: SessionType> Chan<'a, I, E, Send<A, S>> {
    /// Send an `A` to IO.
    pub fn send(self, a: A) -> Chan<'a, I, E, S> {
        self.0.send(a);

        Chan(self.0, PhantomData)
    }
}

impl<'a, I: IO<A>, A, E: SessionType, S: SessionType> Chan<'a, I, E, Recv<A, S>> {
    /// Receive an `A` from IO.
    pub fn recv(self) -> Result<(A, Chan<'a, I, E, S>), Self> {
        match self.0.recv() {
            Some(res) => Ok((res, unsafe { mem::transmute(self) })),
            None => {
                Err(self)
            }
        }
    }
}

impl<'a, I, E: SessionType, S: SessionType> Chan<'a, I, E, Nest<S>> {
    /// Enter into a nested protocol.
    pub fn enter(self) -> Chan<'a, I, (S, E), S> {
        Chan(self.0, PhantomData)
    }
}

impl<'a, I, N: Peano, E: SessionType + Pop<N>> Chan<'a, I, E, Escape<N>> {
    /// Escape from a nested protocol.
    pub fn pop(self) -> Chan<'a, I, E::Tail, E::Head> {
        Chan(self.0, PhantomData)
    }
}

impl<'a, I: IO<u8>, E: SessionType, P: SessionType> Chan<'a, I, E, P> {
    /// Select a protocol to advance to.
    pub fn choose<S: SessionType>(self) -> Chan<'a, I, E, S> where P: Chooser<S> {
        self.0.send(P::num());

        Chan(self.0, PhantomData)
    }
}

impl<'a, I: IO<u8>, E: SessionType, S: SessionType, Q: SessionType> Chan<'a, I, E, Accept<S, Q>> {
    /// Accept one of many protocols and advance to its handler.
    pub fn accept<P: Protocol + Handler<I, E, Accept<S, Q>> + Acceptor<I, E, Accept<S, Q>>>(self) -> Defer<P, I> {
        match self.0.recv() {
            Some(num) => {
                <P as Acceptor<I, E, Accept<S, Q>>>::defer(num)
            },
            None => {
                self.defer()
            }
        }
    }
}

struct Dummy<I, P, E, S>(PhantomData<(I, P, E, S)>);
impl<I, P: Protocol, E: SessionType, S: SessionType> Dummy<I, P, E, S> {
    fn with<'a>(_: Chan<'a, I, E, S>) -> Defer<P, I> {
        panic!("Channel was closed!");
    }
}<|endoftext|>