<commit_before>use arch::context::ContextFile;

use core::slice;

use fs::{ResourceSeek, Url};

use schemes::pipe::{PipeRead, PipeWrite};

use system::c_string_to_str;

use syscall::{Stat, SEEK_CUR, SEEK_END, SEEK_SET};

use system::error::{Error, Result, EBADF, EFAULT, EINVAL};

pub fn do_sys_chdir(path: *const u8) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    unsafe {
        *current.cwd.get() = current.canonicalize(c_string_to_str(path));
    }
    Ok(0)
}

pub fn do_sys_close(fd: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());

    //debugln!("{}: {}: close {}", current.pid, current.name, fd);

    for i in 0..unsafe { (*current.files.get()).len() } {
        let mut remove = false;
        if let Some(file) = unsafe { (*current.files.get()).get(i) } {
            if file.fd == fd {
                remove = true;
            }
        }

        if remove {
            if i < unsafe { (*current.files.get()).len() } {
                drop(unsafe { (*current.files.get()).remove(i) });

                return Ok(0);
            }
        }
    }

    Err(Error::new(EBADF))
}

pub fn do_sys_dup(fd: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let resource = try!(current.get_file(fd));
    let new_resource = try!(resource.dup());
    let new_fd = current.next_fd();

    //debugln!("{}: {}: dup {} as {}", current.pid, current.name, fd, new_fd);

    unsafe {
        (*current.files.get()).push(ContextFile {
            fd: new_fd,
            resource: new_resource,
        });
    }
    Ok(new_fd)
}

pub fn do_sys_fpath(fd: usize, buf: *mut u8, count: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let resource = try!(current.get_file(fd));
    resource.path(unsafe { slice::from_raw_parts_mut(buf, count) })
}

pub fn do_sys_fstat(fd: usize, stat: *mut Stat) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let resource = try!(current.get_file(fd));
    if stat as usize > 0 {
        resource.stat(unsafe { &mut *stat })
    } else {
        Err(Error::new(EFAULT))
    }
}

pub fn do_sys_fsync(fd: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.sync().and(Ok(0))
}

pub fn do_sys_ftruncate(fd: usize, len: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.truncate(len).and(Ok(0))
}

//TODO: Link

pub fn do_sys_lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    match whence {
        SEEK_SET => resource.seek(ResourceSeek::Start(offset as usize)),
        SEEK_CUR => resource.seek(ResourceSeek::Current(offset)),
        SEEK_END => resource.seek(ResourceSeek::End(offset)),
        _ => Err(Error::new(EINVAL)),
    }
}

pub fn do_sys_mkdir(path: *const u8, flags: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path_string = current.canonicalize(c_string_to_str(path));
    ::env().mkdir(try!(Url::from_str(&path_string)), flags).and(Ok(0))
}

pub fn do_sys_open(path_c: *const u8, flags: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path = current.canonicalize(c_string_to_str(path_c));
    //debugln!("{}: {}: open {}", current.pid, current.name, path);
    let url = try!(Url::from_str(&path));
    let resource = try!(::env().open(url, flags));
    let fd = current.next_fd();
    unsafe {
        (*current.files.get()).push(ContextFile {
            fd: fd,
            resource: resource,
        });
    }
    Ok(fd)
}

pub fn do_sys_pipe2(fds: *mut usize, _flags: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    if fds as usize > 0 {
        let read = box PipeRead::new();
        let write = box PipeWrite::new(&read);

        unsafe {
            *fds.offset(0) = current.next_fd();
            (*current.files.get()).push(ContextFile {
                fd: *fds.offset(0),
                resource: read,
            });

            *fds.offset(1) = current.next_fd();
            (*current.files.get()).push(ContextFile {
                fd: *fds.offset(1),
                resource: write,
            });
        }

        Ok(0)
    } else {
        Err(Error::new(EFAULT))
    }
}

pub fn do_sys_read(fd: usize, buf: *mut u8, count: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.read(unsafe { slice::from_raw_parts_mut(buf, count) })
}

pub fn do_sys_rmdir(path: *const u8) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path_string = current.canonicalize(c_string_to_str(path));
    ::env().rmdir(try!(Url::from_str(&path_string))).and(Ok(0))
}

pub fn do_sys_stat(path: *const u8, stat: *mut Stat) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path = current.canonicalize(c_string_to_str(path));
    let url = try!(Url::from_str(&path));
    if stat as usize > 0 {
        ::env().stat(url, unsafe { &mut *stat }).and(Ok(0))
    } else {
        Err(Error::new(EFAULT))
    }
}

pub fn do_sys_unlink(path: *const u8) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path_string = current.canonicalize(c_string_to_str(path));
    ::env().unlink(try!(Url::from_str(&path_string))).and(Ok(0))
}

pub fn do_sys_write(fd: usize, buf: *const u8, count: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.write(unsafe { slice::from_raw_parts(buf, count) })
}
<commit_msg>Add some syscall docs<commit_after>use arch::context::ContextFile;

use core::slice;

use fs::{ResourceSeek, Url};

use schemes::pipe::{PipeRead, PipeWrite};

use system::c_string_to_str;

use syscall::{Stat, SEEK_CUR, SEEK_END, SEEK_SET};

use system::error::{Error, Result, EBADF, EFAULT, EINVAL};

/** <!-- @MANSTART{sys_chdir} -->
NAME
    sys_chdir - change working directory

SYNOPSIS
    sys_chdir(path: *const u8) -> Result<usize>;

DESCRIPTION
    sys_chdir changes the current working directory of the calling process to the directory
    specified in path

RETURN VALUE
    On success, Ok(0) is returned. On error, Err(err) is returned where err is one of the following
    errors

ERRORS
    EACCESS TODO
        Access permissions denied to one of the path components

    EFAULT TODO
        path points outside the accessible address space of the process

    EIO TODO
        An I/O error occured

    ENOENT TODO
        path references a directory that does not exist

    ENOMEM TODO
        Insufficient kernel memory was available

    ENOTDIR TODO
        A component of path is not a directory

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_chdir(path: *const u8) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    unsafe {
        *current.cwd.get() = current.canonicalize(c_string_to_str(path));
    }
    Ok(0)
}

/** <!-- @MANSTART{sys_close} -->
NAME
    sys_close - close a file descriptor

SYNOPSIS
    sys_close(fd: usize) -> Result<usize>;

DESCRIPTION
    sys_close closes a file descriptor, so that it no longer refers to any file and may be reused.

RETURN VALUE
    On success, Ok(0) is returned. On error, Err(err) is returned where err is one of the following
    errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    EIO TODO
        An I/O error occured

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_close(fd: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());

    //debugln!("{}: {}: close {}", current.pid, current.name, fd);

    for i in 0..unsafe { (*current.files.get()).len() } {
        let mut remove = false;
        if let Some(file) = unsafe { (*current.files.get()).get(i) } {
            if file.fd == fd {
                remove = true;
            }
        }

        if remove {
            if i < unsafe { (*current.files.get()).len() } {
                drop(unsafe { (*current.files.get()).remove(i) });

                return Ok(0);
            }
        }
    }

    Err(Error::new(EBADF))
}

/** <!-- @MANSTART{sys_dup} -->
NAME
    sys_dup - duplicate a file descriptor

SYNOPSIS
    sys_dup(fd: usize) -> Result<usize>;

DESCRIPTION
    sys_dup creates a copy of fd, using the lowest unused descriptor for the new descriptor

RETURN VALUE
    On success, Ok(new_fd) is returned, where new_fd is the new file descriptor. On error, Err(err)
    is returned where err is one of the following errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_dup(fd: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let resource = try!(current.get_file(fd));
    let new_resource = try!(resource.dup());
    let new_fd = current.next_fd();

    //debugln!("{}: {}: dup {} as {}", current.pid, current.name, fd, new_fd);

    unsafe {
        (*current.files.get()).push(ContextFile {
            fd: new_fd,
            resource: new_resource,
        });
    }
    Ok(new_fd)
}

pub fn do_sys_fpath(fd: usize, buf: *mut u8, count: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let resource = try!(current.get_file(fd));
    resource.path(unsafe { slice::from_raw_parts_mut(buf, count) })
}

pub fn do_sys_fstat(fd: usize, stat: *mut Stat) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let resource = try!(current.get_file(fd));
    if stat as usize > 0 {
        resource.stat(unsafe { &mut *stat })
    } else {
        Err(Error::new(EFAULT))
    }
}

/** <!-- @MANSTART{sys_fsync} -->
NAME
    sys_fsync - synchronize a file's in-core state with storage device

SYNOPSIS
    sys_fsync(fd: usize) -> Result<usize>;

DESCRIPTION
    sys_fsync transfers all modified in-core data of the file refered to by the file descriptor fd
    to the underlying device

RETURN VALUE
    On success, Ok(0) is returned. On error, Err(err) is returned where err is one of the following
    errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    EIO TODO
        An I/O error occured

    EINVAL TODO
        fd does not support synchronization

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_fsync(fd: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.sync().and(Ok(0))
}

/** <!-- @MANSTART{sys_ftruncate} -->
NAME
    sys_ftruncate - truncate a file to a specified length

SYNOPSIS
    sys_ftruncate(fd: usize, length: usize) -> Result<usize>;

DESCRIPTION
    sys_ftruncate causes the file referenced by fd to be truncated to a size of precisely length
    bytes

RETURN VALUE
    On success, Ok(0) is returned. On error, Err(err) is returned where err is one of the following
    errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    EIO TODO
        An I/O error occured

    EINVAL TODO
        fd does not support truncation

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_ftruncate(fd: usize, length: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.truncate(length).and(Ok(0))
}

//TODO: Link

/** <!-- @MANSTART{sys_lseek} -->
NAME
    sys_lseek - reposition read/write file offset

SYNOPSIS
    sys_lseek(fd: usize, offset: isize, whence: usize) -> Result<usize>;

DESCRIPTION
    sys_lseek repositions the offset of the file referenced by fd to the offset according to whence

    SEEK_SET: 0
        The offset is set to offset bytes

    SEEK_CUR: 1
        The offset is set to its current location plus offset bytes

    SEEK_END: 2
        The offset is set to the size of the file plus offset bytes

RETURN VALUE
    On success, Ok(new_offset) is returned, where new_offset is the resulting offset location. On
    error, Err(err) is returned where err is one of the following errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    EINVAL
        whence or the offset is not valid

    ESPIPE
        fd does not support seeking

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    match whence {
        SEEK_SET => resource.seek(ResourceSeek::Start(offset as usize)),
        SEEK_CUR => resource.seek(ResourceSeek::Current(offset)),
        SEEK_END => resource.seek(ResourceSeek::End(offset)),
        _ => Err(Error::new(EINVAL)),
    }
}

/** <!-- @MANSTART{sys_mkdir} -->
NAME
    sys_mkdir - create a directory

SYNOPSIS
    sys_mkdir(path: *const u8, flags: usize) -> Result<usize>;

DESCRIPTION
    sys_mkdir attempts to create a directory named path

RETURN VALUE
    On success, Ok(0) is returned. On error, Err(err) is returned where err is one of the following
    errors

ERRORS
    EACCES
        This process does not have write permissions to the parent directory or search permissions
        to other components in path

    EEXIST
        path already exists

    EFAULT
        path points outside of the accessible address space of the process

    ENOENT
        A directory component in path does not exist

    EPERM
        The filesystem containing path does not support the creation of directories

    EROFS
        The filesystem containing path is read-only

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_mkdir(path: *const u8, flags: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path_string = current.canonicalize(c_string_to_str(path));
    ::env().mkdir(try!(Url::from_str(&path_string)), flags).and(Ok(0))
}

/** <!-- @MANSTART{sys_open} -->
NAME
    sys_open - open and possibly create a file

SYNOPSIS
    sys_open(path: *const u8, flags: usize) -> Result<usize>;

DESCRIPTION
    sys_open returns a file descriptor referencing path, creating path if O_CREAT is provided

    TODO: Open is very complicated, and has a lot of flags

RETURN VALUE
    On success, Ok(fd) is returned, where fd is a file descriptor referencing path. On error,
    Err(err) is returned where err is one of the following errors

ERRORS
    EACCES
        The requested access to the file is not allowed, or search permissions are denied for one
        of the components of path, or the file did not exist and write access to the parent
        directory is not allowed

    EEXIST
        path already exists

    EFAULT
        path points outside of the accessible address space of the process

    EISDIR
        path refers to a directory and O_DIRECTORY was not provided

    ENOENT
        A directory component in path does not exist

    ENOMEM
        insufficient kernel memory was available

    ENOSPC
        There was insufficient space to create path

    ENOTDIR
        path does not refer to a directory and O_DIRECTORY was passed

    EPERM
        The filesystem containing path does not support the creation of files

    EROFS
        The filesystem containing path is read-only and write access was requested

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_open(path_c: *const u8, flags: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path = current.canonicalize(c_string_to_str(path_c));
    //debugln!("{}: {}: open {}", current.pid, current.name, path);
    let url = try!(Url::from_str(&path));
    let resource = try!(::env().open(url, flags));
    let fd = current.next_fd();
    unsafe {
        (*current.files.get()).push(ContextFile {
            fd: fd,
            resource: resource,
        });
    }
    Ok(fd)
}

pub fn do_sys_pipe2(fds: *mut usize, _flags: usize) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    if fds as usize > 0 {
        let read = box PipeRead::new();
        let write = box PipeWrite::new(&read);

        unsafe {
            *fds.offset(0) = current.next_fd();
            (*current.files.get()).push(ContextFile {
                fd: *fds.offset(0),
                resource: read,
            });

            *fds.offset(1) = current.next_fd();
            (*current.files.get()).push(ContextFile {
                fd: *fds.offset(1),
                resource: write,
            });
        }

        Ok(0)
    } else {
        Err(Error::new(EFAULT))
    }
}

/** <!-- @MANSTART{sys_read} -->
NAME
    sys_read - read from a file descriptor

SYNOPSIS
    sys_read(fd: usize, buf: *mut u8, count: usize) -> Result<usize>;

DESCRIPTION
    sys_read attempts to read up to count bytes from file descriptor fd into the buffer starting at
    buf

RETURN VALUE
    On success, Ok(count) is returned, where count is the number of bytes read into buf. On error,
    Err(err) is returned where err is one of the following errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    EFAULT
        buf is outside of the accessible address space of the process

    EINVAL
        fd refers to a ifle that does not support reading

    EIO
        I/O error

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_read(fd: usize, buf: *mut u8, count: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.read(unsafe { slice::from_raw_parts_mut(buf, count) })
}

pub fn do_sys_rmdir(path: *const u8) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path_string = current.canonicalize(c_string_to_str(path));
    ::env().rmdir(try!(Url::from_str(&path_string))).and(Ok(0))
}

pub fn do_sys_stat(path: *const u8, stat: *mut Stat) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path = current.canonicalize(c_string_to_str(path));
    let url = try!(Url::from_str(&path));
    if stat as usize > 0 {
        ::env().stat(url, unsafe { &mut *stat }).and(Ok(0))
    } else {
        Err(Error::new(EFAULT))
    }
}

pub fn do_sys_unlink(path: *const u8) -> Result<usize> {
    let contexts = ::env().contexts.lock();
    let current = try!(contexts.current());
    let path_string = current.canonicalize(c_string_to_str(path));
    ::env().unlink(try!(Url::from_str(&path_string))).and(Ok(0))
}

/** <!-- @MANSTART{sys_write} -->
NAME
    sys_write - read from a file descriptor

SYNOPSIS
    sys_write(fd: usize, buf: *mut u8, count: usize) -> Result<usize>;

DESCRIPTION
    sys_write attempts to read up to count bytes from file descriptor fd into the buffer starting at
    buf

RETURN VALUE
    On success, Ok(count) is returned, where count is the number of bytes read into buf. On error,
    Err(err) is returned where err is one of the following errors

ERRORS
    EBADF
        fd is not a valid open file decriptor

    EFAULT
        buf is outside of the accessible address space of the process

    EINVAL
        fd refers to a ifle that does not support writing

    EIO
        I/O error

    ENOSPC
        The filesystem containing fd has no more space

    EPIPE
        fd is connected to a pipe or socket whose reading end is closed

    ESRCH
        Currently not running in a process context (rare, would only happen during kernel init)
<!-- @MANEND --> */
pub fn do_sys_write(fd: usize, buf: *const u8, count: usize) -> Result<usize> {
    let mut contexts = ::env().contexts.lock();
    let mut current = try!(contexts.current_mut());
    let mut resource = try!(current.get_file_mut(fd));
    resource.write(unsafe { slice::from_raw_parts(buf, count) })
}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::iter::Peekable;
use std::rc::Rc;

use mdo::option::{bind, ret};
use expr::Expr;

fn next_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.next()
}

fn is_delimiter(c: char) -> bool {
    c.is_whitespace() || "()\"'".contains(c)
}

fn read_uint(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    let mut acc = String::new();
    let mut c = first;
    acc.push(first);
    while input.peek().unwrap_or(&' ').is_digit(radix) {
        match input.next() {
            Some(x) => c = x,
            None => break
        }
        acc.push(c);
    }
    Some(Expr::Int(isize::from_str_radix(&acc[..], radix).unwrap()))
}

fn read_int(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    match first {
        '0'...'9' => read_uint(input, first, radix),
        _ => mdo!{
            c =<< input.next();
            ret match first {
                '+' => read_uint(input, c, radix),
                '-' => read_uint(input, c, radix).map(|e| match e {Expr::Int(i) => Expr::Int(-i),_ =>e}),
                _   => None
            }
        }
    }
}

fn read_symbol(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = first.to_string();
    while input.peek().map(|c| !is_delimiter(*c)).unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    Some(Expr::Sym(sym))
}

fn read_plus(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_hyphen(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_string(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = String::new();
    // :TODO: treat escapes
    while input.peek().map(|c| *c != '"').unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    Some(Expr::Str(sym))
}

fn read_list(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = next_nonwhitespaces(input, ' ');
    let car =  match c {
        Some(c) => match c {
            ')' => return Some(Expr::Nil),
            _ => read_aux(input, c)
        },
        None => return None
    };
    match input.peek() {
        None => return None,
        Some(c) => ()
    }
    let cdr = read_list(input, '(');
    match (car, cdr) {
        (Some(car), Some(cdr)) => Some(Expr::cons(car, cdr)),
        _ => None
    }

}

fn read_quote(mut input: &mut Peekable<Chars>, first: char)  -> Option<Expr> {
    mdo!{
        v =<< read_aux(input, ' ');
        ret ret(Expr::list2(Expr::Sym("quote".to_string()), v))
    }
}

fn read_function(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    mdo!{
        v =<< read_aux(input, ' ');
        ret ret(Expr::list2(Expr::Sym("function".to_string()), v))
    }
}

fn read_dispatch(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    mdo!{
        v =<< input.next();
        ret match v {
            '\'' => read_function(input, '\''),
            v => panic!("unknown reader macro #{:?}", v)
        }
    }
}

fn read_aux(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let first =  match next_nonwhitespaces(input, first) {
        Some(c) => c,
        None => return None
    };
    match first {
        '0'...'9' => read_uint(input, first, 10),
        '-' => read_hyphen(input, first),
        '+' => read_plus(input, first),
        '(' => read_list(input, first),
        '"' => read_string(input, first),
        '\'' => read_quote(input, first),
        '#' => read_dispatch(input, first),
        _   => read_symbol(input, first)
    }
}

pub fn read(s: &str) -> Expr {
    let mut input = s.chars().peekable();
    match read_aux(&mut input, ' ') {
        Some(ex) => ex,
        None => Expr::EOF
    }
}

#[test]
fn test_read_empty(){
    assert!(read("") == (Expr::EOF));
    assert!(read("(a b") == (Expr::EOF));
}

#[test]
fn test_read_int() {
    assert!(read("0") == (Expr::Int(0)));
    assert!(read("10") == (Expr::Int(10)));
    assert!(read("-10") == (Expr::Int(-10)));
    assert!(read("+10") == (Expr::Int(10)));
}

#[test]
fn test_read_list(){
    assert!(read("()") == (Expr::Nil));
    assert!(read("(1)") == (Expr::cons(Expr::Int(1), Expr::Nil)));
    assert!(read("(1 2)") == (Expr::cons(Expr::Int(1), Expr::cons(Expr::Int(2),Expr::Nil))));
}

#[test]
fn test_read_symbol(){
    assert!(read("symbol") == (Expr::Sym("symbol".to_string())));
    assert!(read("+symbol") == (Expr::Sym("+symbol".to_string())));
    assert!(read("-symbol") == (Expr::Sym("-symbol".to_string())));
    assert!(read("sym-bol") == (Expr::Sym("sym-bol".to_string())));
    assert!(read("symbol2") == (Expr::Sym("symbol2".to_string())));
}

#[test]
fn test_read_string(){
    assert!(read("\"string\"") == (Expr::Str("string".to_string())));
    assert!(read("\"str()ing\"") == (Expr::Str("str()ing".to_string())));
    assert!(read("\"str123ing\"") == (Expr::Str("str123ing".to_string())));
    assert!(read("\"()string\"") == (Expr::Str("()string".to_string())));
    assert!(read("\"123string\"") == (Expr::Str("123string".to_string())));
}

#[test]
fn test_read_quote(){
    assert!(read("'1") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::Int(1))));
    assert!(read("'symbol") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::Sym("symbol".to_string()))));
    assert!(read("'\"string\"") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::Str("string".to_string()))));
    assert!(read("'(1 2)") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::list2(Expr::Int(1), Expr::Int(2)))))
}

#[test]
fn test_read_function(){
    assert!(read("#'1") == (Expr::list2(Expr::Sym("function".to_string()), Expr::Int(1))));
    assert!(read("#'symbol") == (Expr::list2(Expr::Sym("function".to_string()), Expr::Sym("symbol".to_string()))));
    assert!(read("#'\"string\"") == (Expr::list2(Expr::Sym("function".to_string()), Expr::Str("string".to_string()))));
    assert!(read("#'(1 2)") == (Expr::list2(Expr::Sym("function".to_string()), Expr::list2(Expr::Int(1), Expr::Int(2)))))
}
<commit_msg>fix string reader<commit_after>use std::str::Chars;
use std::iter::Peekable;
use std::rc::Rc;

use mdo::option::{bind, ret};
use expr::Expr;

fn next_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.next()
}

fn is_delimiter(c: char) -> bool {
    c.is_whitespace() || "()\"'".contains(c)
}

fn read_uint(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    let mut acc = String::new();
    let mut c = first;
    acc.push(first);
    while input.peek().unwrap_or(&' ').is_digit(radix) {
        match input.next() {
            Some(x) => c = x,
            None => break
        }
        acc.push(c);
    }
    Some(Expr::Int(isize::from_str_radix(&acc[..], radix).unwrap()))
}

fn read_int(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    match first {
        '0'...'9' => read_uint(input, first, radix),
        _ => mdo!{
            c =<< input.next();
            ret match first {
                '+' => read_uint(input, c, radix),
                '-' => read_uint(input, c, radix).map(|e| match e {Expr::Int(i) => Expr::Int(-i),_ =>e}),
                _   => None
            }
        }
    }
}

fn read_symbol(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = first.to_string();
    while input.peek().map(|c| !is_delimiter(*c)).unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    Some(Expr::Sym(sym))
}

fn read_plus(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_hyphen(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_string(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut string = String::new();
    // :TODO: treat escapes
    while true {
        match input.next() {
            Some(c) => match c == '"' {
                true =>  return Some(Expr::Str(string)),
                false => string.push(c)
            },
            None => return None
        }
    };
    // to make compiler happy
    None
}

fn read_list(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = next_nonwhitespaces(input, ' ');
    let car =  match c {
        Some(c) => match c {
            ')' => return Some(Expr::Nil),
            _ => read_aux(input, c)
        },
        None => return None
    };
    match input.peek() {
        None => return None,
        Some(c) => ()
    }
    let cdr = read_list(input, '(');
    match (car, cdr) {
        (Some(car), Some(cdr)) => Some(Expr::cons(car, cdr)),
        _ => None
    }

}

fn read_quote(mut input: &mut Peekable<Chars>, first: char)  -> Option<Expr> {
    mdo!{
        v =<< read_aux(input, ' ');
        ret ret(Expr::list2(Expr::Sym("quote".to_string()), v))
    }
}

fn read_function(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    mdo!{
        v =<< read_aux(input, ' ');
        ret ret(Expr::list2(Expr::Sym("function".to_string()), v))
    }
}

fn read_dispatch(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    mdo!{
        v =<< input.next();
        ret match v {
            '\'' => read_function(input, '\''),
            v => panic!("unknown reader macro #{:?}", v)
        }
    }
}

fn read_aux(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let first =  match next_nonwhitespaces(input, first) {
        Some(c) => c,
        None => return None
    };
    match first {
        '0'...'9' => read_uint(input, first, 10),
        '-' => read_hyphen(input, first),
        '+' => read_plus(input, first),
        '(' => read_list(input, first),
        '"' => read_string(input, first),
        '\'' => read_quote(input, first),
        '#' => read_dispatch(input, first),
        _   => read_symbol(input, first)
    }
}

pub fn read(s: &str) -> Expr {
    let mut input = s.chars().peekable();
    match read_aux(&mut input, ' ') {
        Some(ex) => ex,
        None => Expr::EOF
    }
}

#[test]
fn test_read_empty(){
    assert!(read("") == (Expr::EOF));
    assert!(read("(a b") == (Expr::EOF));
}

#[test]
fn test_read_int() {
    assert!(read("0") == (Expr::Int(0)));
    assert!(read("10") == (Expr::Int(10)));
    assert!(read("-10") == (Expr::Int(-10)));
    assert!(read("+10") == (Expr::Int(10)));
}

#[test]
fn test_read_list(){
    assert!(read("()") == (Expr::Nil));
    assert!(read("(1)") == (Expr::cons(Expr::Int(1), Expr::Nil)));
    assert!(read("(1 2)") == (Expr::cons(Expr::Int(1), Expr::cons(Expr::Int(2),Expr::Nil))));
}

#[test]
fn test_read_symbol(){
    assert!(read("symbol") == (Expr::Sym("symbol".to_string())));
    assert!(read("+symbol") == (Expr::Sym("+symbol".to_string())));
    assert!(read("-symbol") == (Expr::Sym("-symbol".to_string())));
    assert!(read("sym-bol") == (Expr::Sym("sym-bol".to_string())));
    assert!(read("symbol2") == (Expr::Sym("symbol2".to_string())));
}

#[test]
fn test_read_string(){
    assert!(read("\"string\"") == (Expr::Str("string".to_string())));
    assert!(read("\"str()ing\"") == (Expr::Str("str()ing".to_string())));
    assert!(read("\"str123ing\"") == (Expr::Str("str123ing".to_string())));
    assert!(read("\"()string\"") == (Expr::Str("()string".to_string())));
    assert!(read("\"123string\"") == (Expr::Str("123string".to_string())));
    assert!(read("(\"string\")") == (Expr::list1(Expr::Str("string".to_string()))));
}

#[test]
fn test_read_quote(){
    assert!(read("'1") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::Int(1))));
    assert!(read("'symbol") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::Sym("symbol".to_string()))));
    assert!(read("'\"string\"") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::Str("string".to_string()))));
    assert!(read("'(1 2)") == (Expr::list2(Expr::Sym("quote".to_string()), Expr::list2(Expr::Int(1), Expr::Int(2)))))
}

#[test]
fn test_read_function(){
    assert!(read("#'1") == (Expr::list2(Expr::Sym("function".to_string()), Expr::Int(1))));
    assert!(read("#'symbol") == (Expr::list2(Expr::Sym("function".to_string()), Expr::Sym("symbol".to_string()))));
    assert!(read("#'\"string\"") == (Expr::list2(Expr::Sym("function".to_string()), Expr::Str("string".to_string()))));
    assert!(read("#'(1 2)") == (Expr::list2(Expr::Sym("function".to_string()), Expr::list2(Expr::Int(1), Expr::Int(2)))))
}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::iter::Peekable;

use expr::Expr;
use util::*;

macro_rules! try_opt {
    ($e: expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
        )
}

fn next_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.next()
}

fn is_delimiter(c: char) -> bool {
    c.is_whitespace() || "()\"'".contains(c)
}

fn read_uint(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    let mut acc = String::new();
    let mut c;
    acc.push(first);
    while input.peek().unwrap_or(&' ').is_digit(radix) {
        match input.next() {
            Some(x) => c = x,
            None => break
        }
        acc.push(c);
    }
    Some(Expr::Int(isize::from_str_radix(&acc[..], radix).unwrap()))
}

fn read_int(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    match first {
        '0'...'9' => read_uint(input, first, radix),
        _ =>{
            let c = try_opt!(input.next());
            match first {
                '+' => read_uint(input, c, radix),
                '-' => read_uint(input, c, radix).map(|e| match e {Expr::Int(i) => Expr::Int(-i),_ =>e}),
                _   => None
            }
        }
    }
}

fn read_symbol(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = first.to_string();
    while input.peek().map(|c| !is_delimiter(*c)).unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    if sym == "nil" {
        Some(Expr::Nil)
    } else {
        Some(Expr::Sym(sym))
    }
}

fn read_plus(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = try_opt!(input.peek()).clone();
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_hyphen(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = try_opt!(input.peek()).clone();
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_string(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let mut string = String::new();
    // :TODO: treat escapes
    loop {
        let c = try_opt!(input.next());
        match c == '"' {
            true =>  return Some(Expr::Str(string)),
            false => string.push(c)
        }
    };
}

fn read_list(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let c = try_opt!(next_nonwhitespaces(input, ' '));
    let car =  match c {
            ')' => return Some(Expr::Nil),
            _ => read_aux(input, c)
        };
    try_opt!(input.peek());
    let cdr = read_list(input, '(');
    match (car, cdr) {
        (Some(car), Some(cdr)) => Some(cons(car, cdr)),
        _ => None
    }

}

fn read_quote(mut input: &mut Peekable<Chars>, _: char)  -> Option<Expr> {
    let v =  try_opt!(read_aux(input, ' '));
    Some(list2(Expr::Sym("quote".to_string()), v))
}

fn read_function(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let v = try_opt!(read_aux(input, ' '));
    Some(list2(Expr::Sym("function".to_string()), v))
}

fn read_dispatch(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let v = try_opt!(input.next());
    match v {
        '\'' => read_function(input, '\''),
        v => panic!("unknown reader macro #{:?}", v)
    }
}

fn read_aux(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let first =  try_opt!(next_nonwhitespaces(input, first)) ;
    match first {
        '0'...'9' => read_uint(input, first, 10),
        '-' => read_hyphen(input, first),
        '+' => read_plus(input, first),
        '(' => read_list(input, first),
        '"' => read_string(input, first),
        '\'' => read_quote(input, first),
        '#' => read_dispatch(input, first),
        _   => read_symbol(input, first)
    }
}

pub fn read_in(mut input: &mut Peekable<Chars>) -> Option<Expr> {
    read_aux(input, ' ')
}

pub fn read(s: &str) -> Expr {
    let mut input = s.chars().peekable();
    match read_aux(&mut input, ' ') {
        Some(e) => e,
        None => Expr::EOF
    }
}

#[test]
fn test_read_empty(){
    assert_eq!(read(""), (Expr::EOF));
    assert_eq!(read("(a b"), (Expr::EOF));
}

#[test]
fn test_read_nil() {
    assert_eq!(read("nil"), (Expr::Nil));
}

#[test]
fn test_read_int() {
    assert_eq!(read("0"), (Expr::Int(0)));
    assert_eq!(read("10"), (Expr::Int(10)));
    assert_eq!(read("-10"), (Expr::Int(-10)));
    assert_eq!(read("+10"), (Expr::Int(10)));
}

#[test]
fn test_read_list(){
    assert_eq!(read("()"), (Expr::Nil));
    assert_eq!(read("(1)"), (list1(Expr::Int(1))));
    assert_eq!(read("(1 2)"), (list2(Expr::Int(1), Expr::Int(2))));
}

#[test]
fn test_read_symbol(){
    assert_eq!(read("symbol"), (Expr::Sym("symbol".to_string())));
    assert_eq!(read("+symbol"), (Expr::Sym("+symbol".to_string())));
    assert_eq!(read("-symbol"), (Expr::Sym("-symbol".to_string())));
    assert_eq!(read("sym-bol"), (Expr::Sym("sym-bol".to_string())));
    assert_eq!(read("symbol2"), (Expr::Sym("symbol2".to_string())));
}

#[test]
fn test_read_string(){
    assert_eq!(read("\"string\""), (Expr::Str("string".to_string())));
    assert_eq!(read("\"str()ing\""), (Expr::Str("str()ing".to_string())));
    assert_eq!(read("\"str123ing\""), (Expr::Str("str123ing".to_string())));
    assert_eq!(read("\"()string\""), (Expr::Str("()string".to_string())));
    assert_eq!(read("\"123string\""), (Expr::Str("123string".to_string())));
    assert_eq!(read("(\"string\")"), (list1(Expr::Str("string".to_string()))));
}

#[test]
fn test_read_quote(){
    assert_eq!(read("'1"), (list2(Expr::Sym("quote".to_string()), Expr::Int(1))));
    assert_eq!(read("'symbol"), (list2(Expr::Sym("quote".to_string()), Expr::Sym("symbol".to_string()))));
    assert_eq!(read("'\"string\""), (list2(Expr::Sym("quote".to_string()), Expr::Str("string".to_string()))));
    assert_eq!(read("'(1 2)"), (list2(Expr::Sym("quote".to_string()), list2(Expr::Int(1), Expr::Int(2)))))
}

#[test]
fn test_read_function(){
    assert_eq!(read("#'1"), (list2(Expr::Sym("function".to_string()), Expr::Int(1))));
    assert_eq!(read("#'symbol"), (list2(Expr::Sym("function".to_string()), Expr::Sym("symbol".to_string()))));
    assert_eq!(read("#'\"string\""), (list2(Expr::Sym("function".to_string()), Expr::Str("string".to_string()))));
    assert_eq!(read("#'(1 2)"), (list2(Expr::Sym("function".to_string()), list2(Expr::Int(1), Expr::Int(2)))))
}
<commit_msg>implement dot pair reader<commit_after>use std::str::Chars;
use std::iter::Peekable;

use expr::Expr;
use util::*;

macro_rules! try_opt {
    ($e: expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
        )
}

fn next_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.next()
}

fn peek_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.peek().map(|c| c.clone())
}

fn is_delimiter(c: char) -> bool {
    c.is_whitespace() || "()\"'".contains(c)
}

fn read_uint(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    let mut acc = String::new();
    let mut c;
    acc.push(first);
    while input.peek().unwrap_or(&' ').is_digit(radix) {
        match input.next() {
            Some(x) => c = x,
            None => break
        }
        acc.push(c);
    }
    Some(Expr::Int(isize::from_str_radix(&acc[..], radix).unwrap()))
}

fn read_int(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    match first {
        '0'...'9' => read_uint(input, first, radix),
        _ =>{
            let c = try_opt!(input.next());
            match first {
                '+' => read_uint(input, c, radix),
                '-' => read_uint(input, c, radix).map(|e| match e {Expr::Int(i) => Expr::Int(-i),_ =>e}),
                _   => None
            }
        }
    }
}

fn read_symbol(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = first.to_string();
    while input.peek().map(|c| !is_delimiter(*c)).unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    if sym == "nil" {
        Some(Expr::Nil)
    } else {
        Some(Expr::Sym(sym))
    }
}

fn read_plus(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = try_opt!(input.peek()).clone();
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_hyphen(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = try_opt!(input.peek()).clone();
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_string(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let mut string = String::new();
    // :TODO: treat escapes
    loop {
        let c = try_opt!(input.next());
        match c == '"' {
            true =>  return Some(Expr::Str(string)),
            false => string.push(c)
        }
    };
}

fn read_list(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let c = try_opt!(next_nonwhitespaces(input, ' '));
    let car =  match c {
        ')' => return Some(Expr::Nil),
        _ => read_aux(input, c)
    };

    let c = try_opt!(peek_nonwhitespaces(input, ' '));
    let cdr = if c == '.' {
        let _ = try_opt!(next_nonwhitespaces(input, ' '));
        read_aux(input, ' ')
    } else {
        read_list(input, '(')
    };
    match (car, cdr) {
        (Some(car), Some(cdr)) => Some(cons(car, cdr)),
        _ => None
    }

}

fn read_quote(mut input: &mut Peekable<Chars>, _: char)  -> Option<Expr> {
    let v =  try_opt!(read_aux(input, ' '));
    Some(list2(Expr::Sym("quote".to_string()), v))
}

fn read_function(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let v = try_opt!(read_aux(input, ' '));
    Some(list2(Expr::Sym("function".to_string()), v))
}

fn read_dispatch(mut input: &mut Peekable<Chars>, _: char) -> Option<Expr> {
    let v = try_opt!(input.next());
    match v {
        '\'' => read_function(input, '\''),
        v => panic!("unknown reader macro #{:?}", v)
    }
}

fn read_aux(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let first =  try_opt!(next_nonwhitespaces(input, first)) ;
    match first {
        '0'...'9' => read_uint(input, first, 10),
        '-' => read_hyphen(input, first),
        '+' => read_plus(input, first),
        '(' => read_list(input, first),
        '"' => read_string(input, first),
        '\'' => read_quote(input, first),
        '#' => read_dispatch(input, first),
        _   => read_symbol(input, first)
    }
}

pub fn read_in(mut input: &mut Peekable<Chars>) -> Option<Expr> {
    read_aux(input, ' ')
}

pub fn read(s: &str) -> Expr {
    let mut input = s.chars().peekable();
    match read_aux(&mut input, ' ') {
        Some(e) => e,
        None => Expr::EOF
    }
}

#[test]
fn test_read_empty(){
    assert_eq!(read(""), (Expr::EOF));
    assert_eq!(read("(a b"), (Expr::EOF));
}

#[test]
fn test_read_nil() {
    assert_eq!(read("nil"), (Expr::Nil));
}

#[test]
fn test_read_int() {
    assert_eq!(read("0"), (Expr::Int(0)));
    assert_eq!(read("10"), (Expr::Int(10)));
    assert_eq!(read("-10"), (Expr::Int(-10)));
    assert_eq!(read("+10"), (Expr::Int(10)));
}

#[test]
fn test_read_list(){
    assert_eq!(read("()"), (Expr::Nil));
    assert_eq!(read("(1)"), (list1(Expr::Int(1))));
    assert_eq!(read("(1 2)"), (list2(Expr::Int(1), Expr::Int(2))));
    assert_eq!(read("(1 . 2)"), (cons(Expr::Int(1), Expr::Int(2))));
    assert_eq!(read("(1 2 . 3)"), (cons(Expr::Int(1), cons(Expr::Int(2), Expr::Int(3)))));
}

#[test]
fn test_read_symbol(){
    assert_eq!(read("symbol"), (Expr::Sym("symbol".to_string())));
    assert_eq!(read("+symbol"), (Expr::Sym("+symbol".to_string())));
    assert_eq!(read("-symbol"), (Expr::Sym("-symbol".to_string())));
    assert_eq!(read("sym-bol"), (Expr::Sym("sym-bol".to_string())));
    assert_eq!(read("symbol2"), (Expr::Sym("symbol2".to_string())));
}

#[test]
fn test_read_string(){
    assert_eq!(read("\"string\""), (Expr::Str("string".to_string())));
    assert_eq!(read("\"str()ing\""), (Expr::Str("str()ing".to_string())));
    assert_eq!(read("\"str123ing\""), (Expr::Str("str123ing".to_string())));
    assert_eq!(read("\"()string\""), (Expr::Str("()string".to_string())));
    assert_eq!(read("\"123string\""), (Expr::Str("123string".to_string())));
    assert_eq!(read("(\"string\")"), (list1(Expr::Str("string".to_string()))));
}

#[test]
fn test_read_quote(){
    assert_eq!(read("'1"), (list2(Expr::Sym("quote".to_string()), Expr::Int(1))));
    assert_eq!(read("'symbol"), (list2(Expr::Sym("quote".to_string()), Expr::Sym("symbol".to_string()))));
    assert_eq!(read("'\"string\""), (list2(Expr::Sym("quote".to_string()), Expr::Str("string".to_string()))));
    assert_eq!(read("'(1 2)"), (list2(Expr::Sym("quote".to_string()), list2(Expr::Int(1), Expr::Int(2)))))
}

#[test]
fn test_read_function(){
    assert_eq!(read("#'1"), (list2(Expr::Sym("function".to_string()), Expr::Int(1))));
    assert_eq!(read("#'symbol"), (list2(Expr::Sym("function".to_string()), Expr::Sym("symbol".to_string()))));
    assert_eq!(read("#'\"string\""), (list2(Expr::Sym("function".to_string()), Expr::Str("string".to_string()))));
    assert_eq!(read("#'(1 2)"), (list2(Expr::Sym("function".to_string()), list2(Expr::Int(1), Expr::Int(2)))))
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use traits::Coord;
use traits::ReprConsumer;

#[derive(Debug)]
pub struct CellRepr<'a, C: Coord> {
    pub coord: C,
    pub state: HashMap<&'a str, &'a str>
}

impl<'a, C: Coord> CellRepr<'a, C> {

    pub fn new(coord: C, state: Option<&HashMap<&'a str, &'a str>>) -> Self {

        let state = match state {
            Some(map) => map.clone(),
            None => HashMap::new()
        };

        CellRepr {
            coord: coord,
            state: state
        }
    }
}

#[derive(Debug)]
pub struct GridRepr<'a, C: Coord> {
    pub rows: i32,
    pub cols: i32,
    pub cells: Vec<CellRepr<'a, C>>
}

impl<'a, C: Coord> GridRepr<'a, C> {

    pub fn new<'b>(rows: i32,
                   cols: i32,
                   cells: Option<Vec<CellRepr<'a, C>>>)
        -> Self {

        let cells = match cells {
            Some(cs) => cs,
            None => {
                let len = (rows * cols) as usize;
                Vec::with_capacity(len)
            }
        };

        GridRepr {
            rows: rows,
            cols: cols,
            cells: cells
        }
    }
}

pub struct SimpleConsumer;

impl SimpleConsumer {

    pub fn new() -> Self {
        SimpleConsumer
    }
}

impl ReprConsumer for SimpleConsumer {

    fn consume<C: Coord>(&mut self, repr: &GridRepr<C>) {

        for cell in repr.cells.iter() {
            println!("{} {}", cell.coord.x(), cell.coord.y());
            for (state, value) in &cell.state {
                println!("{}: {}", state, value);
            }
        }
    }
}<commit_msg>Removed unnecessary structs for representation.<commit_after><|endoftext|>


<commit_before>//! Defines structs for operating on ISPC task groups and getting chunks
//! of a task to be scheduled on to threads

use libc;
use aligned_alloc;

use std::cmp;
use std::iter::Iterator;
use std::sync::{Mutex, RwLock, Arc};
use std::sync::atomic::{self, AtomicBool, ATOMIC_BOOL_INIT};

/// A pointer to an ISPC task function.
///
/// The ISPC task function pointer is:
/// ```c
/// void (*TaskFuncPtr)(void *data, int threadIndex, int threadCount,
///                     int taskIndex, int taskCount,
///                     int taskIndex0, int taskIndex1, int taskIndex2,
///                     int taskCount0, int taskCount1, int taskCount2);
/// ```
pub type ISPCTaskFn = extern "C" fn(data: *mut libc::c_void, thread_idx: libc::c_int, thread_cnt: libc::c_int,
                                    task_idx: libc::c_int, task_cnt: libc::c_int, task_idx0: libc::c_int,
                                    task_idx1: libc::c_int, task_idx2: libc::c_int, task_cnt0: libc::c_int,
                                    task_cnt1: libc::c_int, task_cnt2: libc::c_int);

/// A list of all task groups spawned by a function in some launch context which
/// will be sync'd at an explicit `sync` call or function exit.
///
/// **Note:** A Context is done if and only if ISPCSync has been called with
/// its handle and all of its tasks are finished. Until ISPCSync is called on the
/// Context's handle more tasks could be launched.
///
/// Additionally, because we're not really able to associate a call to ISPCAlloc
/// with a specific Group care must be taken that the Context is not dropped
/// until ISPCSync has been called on its handle and all Groups within have
/// completed execution.
#[derive(Debug)]
pub struct Context {
    /// Task groups launched by this function
    /// TODO: Must be protected by a Reader-Writer lock, though I don't think we'd want to
    /// protect each Group, it'd be an RwLock<Vec<Group>>
    /// PROBLEM: If we're accessing this from multiple threads and have other threads
    /// working on the group when we want to push a new group on we'll get stuck until those
    /// tasks finish because they'll have a read lock on the vec to access the Group safely.
    /// I guess an easy fix would be to push groups behind Arcs? But then how would the
    /// Chunk get the Arc?
    tasks: RwLock<Vec<Arc<Group>>>,
    /// The memory allocated for the various task group's parameters
    mem: Mutex<Vec<*mut libc::c_void>>,
    pub id: usize,
}

impl Context {
    /// Create a new list of tasks for some function with id `id`
    pub fn new(id: usize) -> Context {
        Context { tasks: RwLock::new(Vec::new()), mem: Mutex::new(Vec::new()), id: id }
    }
    /// Add a task group for execution that was launched in this context
    pub fn launch(&self, total: (i32, i32, i32), data: *mut libc::c_void, fcn: ISPCTaskFn) {
        self.tasks.write().unwrap().push(Arc::new(Group::new(total, data, fcn)));
    }
    /// Check if all tasks currently in the task list are completed
    ///
    /// **Note:** A Context is done if and only if ISPCSync has been called with
    /// its handle and all of its tasks are finished. Until ISPCSync is called on the
    /// Context's handle more tasks could be launched.
    /// TODO: With this design we're essentially requiring the thread waiting on the context
    /// to busy wait since we provide no condition variable to block on.
    pub fn current_tasks_done(&self) -> bool {
        self.tasks.read().unwrap()
            .iter().fold(true, |done, t| {
            done && t.finished.load(atomic::Ordering::SeqCst)
        })
    }
    /// Allocate some memory for this Context's task groups, returns a pointer to the allocated memory.
    pub unsafe fn alloc(&self, size: usize, align: usize) -> *mut libc::c_void {
        // TODO: The README for this lib mentions it may be slow. Maybe use some other allocator?
        let ptr = aligned_alloc::aligned_alloc(size as usize, align as usize) as *mut libc::c_void;
        let mut mem = self.mem.lock().unwrap();
        mem.push(ptr);
        ptr
    }
    /// An iterator over the **current** groups in the context which have remaining tasks to
    /// run on a thread. If more task groups are added before this iterator has returned
    /// None those will appear as well.
    pub fn iter(&self) -> ContextIter {
        ContextIter { context: self }
    }
    /// Get a Group with tasks remaining to be executed, returns None if there
    /// are no groups left to run in this context.
    ///
    /// Note that you can't assume that the Group you get back is guaranteed
    /// to have tasks remaining since between the time of checking that the
    /// group has outstanding tasks and getting the group back to call `chunks`
    /// those remaining tasks may have been taken by another threaad.
    fn get_active_group(&self) -> Option<Arc<Group>> {
        let tasks = self.tasks.read().unwrap();
        for group in tasks.iter() {
            if group.has_tasks() {
                return Some(group.clone());
            }
        }
        None
    }
}

impl Drop for Context {
    /// Release memory for all the tasks in this context
    ///
    /// **Note:** that because we're not really able to associate a call to ISPCAlloc
    /// with a specific Group care must be taken that the Context is not dropped
    /// until ISPCSync has been called on its handle and all Groups within have
    /// completed execution.
    fn drop(&mut self) {
        let mut mem = self.mem.lock().unwrap();
        for m in mem.drain(0..) {
            unsafe { aligned_alloc::aligned_free(m as *mut ()); }
        }
    }
}

/// An iterator over the **current** groups in the context which have remaining tasks to
/// run on a thread. If more task groups are added before this iterator has returned
/// None those will appear as well.
pub struct ContextIter<'a> {
    context: &'a Context,
}

impl<'a> Iterator for ContextIter<'a> {
    type Item = Arc<Group>;

    /// Get a Group with tasks remaining to be executed, returns None if there
    /// are no groups left to run in this context.
    ///
    /// Note that you can't assume that the Group you get back is guaranteed
    /// to have tasks remaining since between the time of checking that the
    /// group has outstanding tasks and getting the group back to call `chunks`
    /// those remaining tasks may have been taken by another threaad.
    fn next(&mut self) -> Option<Arc<Group>> {
        self.context.get_active_group()
    }
}

/// A group of tasks spawned by a call to `launch` in ISPC
#[derive(Debug)]
pub struct Group {
    /// Current starting index to execute the remaining tasks in this group
    /// TODO: Protect start by a mutex since it will be modified by `get_chunk`
    /// which would get a chunk of tasks to be executed along with a copy of the
    /// total, fcn ptr and data. This would be wrapped in to a struct, `Chunk` which
    /// would expose next() and behave like an iterator to go through the chunk of tasks
    /// and run them. Right now we just schedule tasks like in a nested for loop,
    /// would some tiled scheduling be better?
    pub start: RwLock<i32>,
    /// Total number of tasks scheduled in this group
    pub total: (i32, i32, i32),
    /// Function to run for this task
    pub fcn: ISPCTaskFn,
    /// Data pointer to user params to pass to the function
    pub data: *mut libc::c_void,
    /// Whether all tasks have been completed or not, TODO: should become
    /// an atomic or semaphore
    /// I'm unsure whether an atomic or semaphore would be the better choice here
    /// The TASK_LIST would want to send an alert when new tasks are pushed so in
    /// Sync we could wait on the context to finish?
    finished: AtomicBool,
}

impl Group {
    /// Create a new task group for execution of the function
    pub fn new(total: (i32, i32, i32), data: *mut libc::c_void, fcn: ISPCTaskFn) -> Group {
        Group { start: RwLock::new(0), total: total, data: data, fcn: fcn, finished: ATOMIC_BOOL_INIT }
    }
    pub fn chunks(&self, chunk_size: i32) -> GroupChunks {
        GroupChunks { group: self, chunk_size: chunk_size }
    }
    pub fn is_finished(&self) -> bool {
        self.finished.load(atomic::Ordering::SeqCst)
    }
    fn mark_finished(&self) {
        self.finished.store(true, atomic::Ordering::SeqCst)
    }
    /// Check if this group has tasks left to execute
    fn has_tasks(&self) -> bool {
        let end = self.total.0 * self.total.1 * self.total.2;
        let start = self.start.read().unwrap();
        *start < end
    }
    /// Get a chunk of tasks from the group to run if there are any tasks left to run
    ///
    /// `desired_tasks` specifies the number of tasks we'd like the chunk to contain,
    /// though you may get fewer if there aren't that many tasks left. If the chunk
    /// you get is the last chunk to be executed (`chunk.end == total.0 * total.1 * total.2`)
    /// you must mark this group as finished upon completing execution of the chunk
    fn get_chunk(&self, desired_tasks: i32) -> Option<Chunk> {
        let end = self.total.0 * self.total.1 * self.total.2;
        let mut start = self.start.write().unwrap();
        if *start < end {
            // Give the chunk 4 tasks or whatever remain
            let c = Some(Chunk::new(self, *start, cmp::min(*start + desired_tasks, end)));
            *start += desired_tasks;
            c
        } else {
            None
        }
    }
}

/// An iterator over chunks of tasks to be executed in a Group
pub struct GroupChunks<'a> {
    group: &'a Group,
    chunk_size: i32,
}

impl<'a> Iterator for GroupChunks<'a> {
    type Item = Chunk<'a>;

    /// Get the next chunk of tasks to be executed
    fn next(&mut self) -> Option<Chunk<'a>> {
        self.group.get_chunk(self.chunk_size)
    }
}

/// A chunk of tasks from a Group to be executed
///
/// Executes task in the range [start, end)
#[derive(Debug)]
pub struct Chunk<'a> {
    /// The next task to be executed in this chunk
    start: i32,
    /// The last task to be executed in this chunk
    end: i32,
    /// Total number of tasks scheduled in the group this chunk came from
    total: (i32, i32, i32),
    /// Function to run for this task
    fcn: ISPCTaskFn,
    /// Data pointer to user params to pass to the function
    data: *mut libc::c_void,
    /// The group this chunk is running tasks from
    group: &'a Group,
}

impl<'a> Chunk<'a> {
    /// Create a new chunk to execute tasks in the group from [start, end)
    pub fn new(group: &'a Group, start: i32, end: i32) -> Chunk {
        Chunk { start: start, end: end, total: group.total,
                fcn: group.fcn, data: group.data, group: group
        }
    }
    /// Execute all tasks in this chunk
    pub fn execute(&self, thread_id: i32, total_threads: i32) {
        let total_tasks = self.total.0 * self.total.1 * self.total.2;
        for t in self.start..self.end {
            let id = self.task_indices(t);
            (self.fcn)(self.data, thread_id as libc::c_int, total_threads as libc::c_int,
                       t as libc::c_int, total_tasks as libc::c_int,
                       id.0 as libc::c_int, id.1 as libc::c_int, id.2 as libc::c_int,
                       self.total.0 as libc::c_int, self.total.1 as libc::c_int,
                       self.total.2 as libc::c_int);
        }
        // If this chunk finished the group mark the group as finished
        if self.is_last() {
            self.group.mark_finished();
        }
    }
    /// Check if this is the last chunk in the group
    pub fn is_last(&self) -> bool {
        self.end == self.total.0 * self.total.1 * self.total.2
    }
    /// Get the global task id for the task index
    fn task_indices(&self, id: i32) -> (i32, i32, i32) {
        (id % self.total.0, (id / self.total.0) % self.total.1, id / (self.total.0 * self.total.1))
    }
}

<commit_msg>Remove trailing space<commit_after>//! Defines structs for operating on ISPC task groups and getting chunks
//! of a task to be scheduled on to threads

use libc;
use aligned_alloc;

use std::cmp;
use std::iter::Iterator;
use std::sync::{Mutex, RwLock, Arc};
use std::sync::atomic::{self, AtomicBool, ATOMIC_BOOL_INIT};

/// A pointer to an ISPC task function.
///
/// The ISPC task function pointer is:
/// ```c
/// void (*TaskFuncPtr)(void *data, int threadIndex, int threadCount,
///                     int taskIndex, int taskCount,
///                     int taskIndex0, int taskIndex1, int taskIndex2,
///                     int taskCount0, int taskCount1, int taskCount2);
/// ```
pub type ISPCTaskFn = extern "C" fn(data: *mut libc::c_void, thread_idx: libc::c_int, thread_cnt: libc::c_int,
                                    task_idx: libc::c_int, task_cnt: libc::c_int, task_idx0: libc::c_int,
                                    task_idx1: libc::c_int, task_idx2: libc::c_int, task_cnt0: libc::c_int,
                                    task_cnt1: libc::c_int, task_cnt2: libc::c_int);

/// A list of all task groups spawned by a function in some launch context which
/// will be sync'd at an explicit `sync` call or function exit.
///
/// **Note:** A Context is done if and only if ISPCSync has been called with
/// its handle and all of its tasks are finished. Until ISPCSync is called on the
/// Context's handle more tasks could be launched.
///
/// Additionally, because we're not really able to associate a call to ISPCAlloc
/// with a specific Group care must be taken that the Context is not dropped
/// until ISPCSync has been called on its handle and all Groups within have
/// completed execution.
#[derive(Debug)]
pub struct Context {
    /// Task groups launched by this function
    /// TODO: Must be protected by a Reader-Writer lock, though I don't think we'd want to
    /// protect each Group, it'd be an RwLock<Vec<Group>>
    /// PROBLEM: If we're accessing this from multiple threads and have other threads
    /// working on the group when we want to push a new group on we'll get stuck until those
    /// tasks finish because they'll have a read lock on the vec to access the Group safely.
    /// I guess an easy fix would be to push groups behind Arcs? But then how would the
    /// Chunk get the Arc?
    tasks: RwLock<Vec<Arc<Group>>>,
    /// The memory allocated for the various task group's parameters
    mem: Mutex<Vec<*mut libc::c_void>>,
    pub id: usize,
}

impl Context {
    /// Create a new list of tasks for some function with id `id`
    pub fn new(id: usize) -> Context {
        Context { tasks: RwLock::new(Vec::new()), mem: Mutex::new(Vec::new()), id: id }
    }
    /// Add a task group for execution that was launched in this context
    pub fn launch(&self, total: (i32, i32, i32), data: *mut libc::c_void, fcn: ISPCTaskFn) {
        self.tasks.write().unwrap().push(Arc::new(Group::new(total, data, fcn)));
    }
    /// Check if all tasks currently in the task list are completed
    ///
    /// **Note:** A Context is done if and only if ISPCSync has been called with
    /// its handle and all of its tasks are finished. Until ISPCSync is called on the
    /// Context's handle more tasks could be launched.
    /// TODO: With this design we're essentially requiring the thread waiting on the context
    /// to busy wait since we provide no condition variable to block on.
    pub fn current_tasks_done(&self) -> bool {
        self.tasks.read().unwrap()
            .iter().fold(true, |done, t| {
            done && t.finished.load(atomic::Ordering::SeqCst)
        })
    }
    /// Allocate some memory for this Context's task groups, returns a pointer to the allocated memory.
    pub unsafe fn alloc(&self, size: usize, align: usize) -> *mut libc::c_void {
        // TODO: The README for this lib mentions it may be slow. Maybe use some other allocator?
        let ptr = aligned_alloc::aligned_alloc(size as usize, align as usize) as *mut libc::c_void;
        let mut mem = self.mem.lock().unwrap();
        mem.push(ptr);
        ptr
    }
    /// An iterator over the **current** groups in the context which have remaining tasks to
    /// run on a thread. If more task groups are added before this iterator has returned
    /// None those will appear as well.
    pub fn iter(&self) -> ContextIter {
        ContextIter { context: self }
    }
    /// Get a Group with tasks remaining to be executed, returns None if there
    /// are no groups left to run in this context.
    ///
    /// Note that you can't assume that the Group you get back is guaranteed
    /// to have tasks remaining since between the time of checking that the
    /// group has outstanding tasks and getting the group back to call `chunks`
    /// those remaining tasks may have been taken by another threaad.
    fn get_active_group(&self) -> Option<Arc<Group>> {
        let tasks = self.tasks.read().unwrap();
        for group in tasks.iter() {
            if group.has_tasks() {
                return Some(group.clone());
            }
        }
        None
    }
}

impl Drop for Context {
    /// Release memory for all the tasks in this context
    ///
    /// **Note:** that because we're not really able to associate a call to ISPCAlloc
    /// with a specific Group care must be taken that the Context is not dropped
    /// until ISPCSync has been called on its handle and all Groups within have
    /// completed execution.
    fn drop(&mut self) {
        let mut mem = self.mem.lock().unwrap();
        for m in mem.drain(0..) {
            unsafe { aligned_alloc::aligned_free(m as *mut ()); }
        }
    }
}

/// An iterator over the **current** groups in the context which have remaining tasks to
/// run on a thread. If more task groups are added before this iterator has returned
/// None those will appear as well.
pub struct ContextIter<'a> {
    context: &'a Context,
}

impl<'a> Iterator for ContextIter<'a> {
    type Item = Arc<Group>;

    /// Get a Group with tasks remaining to be executed, returns None if there
    /// are no groups left to run in this context.
    ///
    /// Note that you can't assume that the Group you get back is guaranteed
    /// to have tasks remaining since between the time of checking that the
    /// group has outstanding tasks and getting the group back to call `chunks`
    /// those remaining tasks may have been taken by another threaad.
    fn next(&mut self) -> Option<Arc<Group>> {
        self.context.get_active_group()
    }
}

/// A group of tasks spawned by a call to `launch` in ISPC
#[derive(Debug)]
pub struct Group {
    /// Current starting index to execute the remaining tasks in this group
    /// TODO: Protect start by a mutex since it will be modified by `get_chunk`
    /// which would get a chunk of tasks to be executed along with a copy of the
    /// total, fcn ptr and data. This would be wrapped in to a struct, `Chunk` which
    /// would expose next() and behave like an iterator to go through the chunk of tasks
    /// and run them. Right now we just schedule tasks like in a nested for loop,
    /// would some tiled scheduling be better?
    pub start: RwLock<i32>,
    /// Total number of tasks scheduled in this group
    pub total: (i32, i32, i32),
    /// Function to run for this task
    pub fcn: ISPCTaskFn,
    /// Data pointer to user params to pass to the function
    pub data: *mut libc::c_void,
    /// Whether all tasks have been completed or not, TODO: should become
    /// an atomic or semaphore
    /// I'm unsure whether an atomic or semaphore would be the better choice here
    /// The TASK_LIST would want to send an alert when new tasks are pushed so in
    /// Sync we could wait on the context to finish?
    finished: AtomicBool,
}

impl Group {
    /// Create a new task group for execution of the function
    pub fn new(total: (i32, i32, i32), data: *mut libc::c_void, fcn: ISPCTaskFn) -> Group {
        Group { start: RwLock::new(0), total: total, data: data, fcn: fcn, finished: ATOMIC_BOOL_INIT }
    }
    pub fn chunks(&self, chunk_size: i32) -> GroupChunks {
        GroupChunks { group: self, chunk_size: chunk_size }
    }
    pub fn is_finished(&self) -> bool {
        self.finished.load(atomic::Ordering::SeqCst)
    }
    fn mark_finished(&self) {
        self.finished.store(true, atomic::Ordering::SeqCst)
    }
    /// Check if this group has tasks left to execute
    fn has_tasks(&self) -> bool {
        let end = self.total.0 * self.total.1 * self.total.2;
        let start = self.start.read().unwrap();
        *start < end
    }
    /// Get a chunk of tasks from the group to run if there are any tasks left to run
    ///
    /// `desired_tasks` specifies the number of tasks we'd like the chunk to contain,
    /// though you may get fewer if there aren't that many tasks left. If the chunk
    /// you get is the last chunk to be executed (`chunk.end == total.0 * total.1 * total.2`)
    /// you must mark this group as finished upon completing execution of the chunk
    fn get_chunk(&self, desired_tasks: i32) -> Option<Chunk> {
        let end = self.total.0 * self.total.1 * self.total.2;
        let mut start = self.start.write().unwrap();
        if *start < end {
            // Give the chunk 4 tasks or whatever remain
            let c = Some(Chunk::new(self, *start, cmp::min(*start + desired_tasks, end)));
            *start += desired_tasks;
            c
        } else {
            None
        }
    }
}

/// An iterator over chunks of tasks to be executed in a Group
pub struct GroupChunks<'a> {
    group: &'a Group,
    chunk_size: i32,
}

impl<'a> Iterator for GroupChunks<'a> {
    type Item = Chunk<'a>;

    /// Get the next chunk of tasks to be executed
    fn next(&mut self) -> Option<Chunk<'a>> {
        self.group.get_chunk(self.chunk_size)
    }
}

/// A chunk of tasks from a Group to be executed
///
/// Executes task in the range [start, end)
#[derive(Debug)]
pub struct Chunk<'a> {
    /// The next task to be executed in this chunk
    start: i32,
    /// The last task to be executed in this chunk
    end: i32,
    /// Total number of tasks scheduled in the group this chunk came from
    total: (i32, i32, i32),
    /// Function to run for this task
    fcn: ISPCTaskFn,
    /// Data pointer to user params to pass to the function
    data: *mut libc::c_void,
    /// The group this chunk is running tasks from
    group: &'a Group,
}

impl<'a> Chunk<'a> {
    /// Create a new chunk to execute tasks in the group from [start, end)
    pub fn new(group: &'a Group, start: i32, end: i32) -> Chunk {
        Chunk { start: start, end: end, total: group.total,
                fcn: group.fcn, data: group.data, group: group
        }
    }
    /// Execute all tasks in this chunk
    pub fn execute(&self, thread_id: i32, total_threads: i32) {
        let total_tasks = self.total.0 * self.total.1 * self.total.2;
        for t in self.start..self.end {
            let id = self.task_indices(t);
            (self.fcn)(self.data, thread_id as libc::c_int, total_threads as libc::c_int,
                       t as libc::c_int, total_tasks as libc::c_int,
                       id.0 as libc::c_int, id.1 as libc::c_int, id.2 as libc::c_int,
                       self.total.0 as libc::c_int, self.total.1 as libc::c_int,
                       self.total.2 as libc::c_int);
        }
        // If this chunk finished the group mark the group as finished
        if self.is_last() {
            self.group.mark_finished();
        }
    }
    /// Check if this is the last chunk in the group
    pub fn is_last(&self) -> bool {
        self.end == self.total.0 * self.total.1 * self.total.2
    }
    /// Get the global task id for the task index
    fn task_indices(&self, id: i32) -> (i32, i32, i32) {
        (id % self.total.0, (id / self.total.0) % self.total.1, id / (self.total.0 * self.total.1))
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2014 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;

use std::clone::Clone;
use std::result::{Ok, Err};
use std::option::{Some};
use std::task::try_future;
use std::io::net::ip::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::iter::Iterator;

use datalink::{datalink_channel, DataLinkChannelType};
use packet::Packet;
use packet::ethernet::{EtherTypes, EthernetHeader, MutableEthernetHeader, EthernetPacket};
use packet::ip::{IpNextHeaderProtocols, IpNextHeaderProtocol};
use packet::ipv4::{Ipv4Header, MutableIpv4Header, Ipv4Packet};
use packet::ipv6::{MutableIpv6Header, Ipv6Packet};
use packet::udp::{UdpHeader, MutableUdpHeader, UdpPacket};
use transport::{udp_header_iter, ipv4_header_iter, transport_channel, TransportProtocol,
                TransportChannelType};
use transport::TransportProtocol::{Ipv4, Ipv6};
use util::NetworkInterface;

const MIN_PACKET_SIZE: uint = 64;
const ETHERNET_HEADER_LEN: uint = 14;
const IPV4_HEADER_LEN: uint = 20;
const IPV6_HEADER_LEN: uint = 40;
const UDP_HEADER_LEN: uint = 8;
const TEST_DATA_LEN: uint = 4;

const IPV4_SOURCE: IpAddr = Ipv4Addr(127, 0, 0, 1);
const IPV4_DESTINATION: IpAddr = Ipv4Addr(127, 0, 0, 1);
const IPV6_SOURCE: IpAddr = Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1);
const IPV6_DESTINATION: IpAddr = Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1);

// Use a protocol which is unlikely to have other packets on
const TEST_PROTO: IpNextHeaderProtocol = IpNextHeaderProtocols::Test1;

fn build_ipv4_header(packet: &mut [u8], offset: uint) {
    let mut ip_header = MutableIpv4Header::new(packet.slice_from_mut(offset));

    let total_len = (IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN) as u16;

    ip_header.set_version(4);
    ip_header.set_header_length(5);
    ip_header.set_total_length(total_len);
    ip_header.set_ttl(4);
    ip_header.set_next_level_protocol(TEST_PROTO);
    ip_header.set_source(IPV4_SOURCE);
    ip_header.set_destination(IPV4_DESTINATION);
    ip_header.checksum();
}

fn build_ipv6_header(packet: &mut [u8], offset: uint) {
    let mut ip_header = MutableIpv6Header::new(packet.slice_from_mut(offset));

    ip_header.set_version(6);
    ip_header.set_payload_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
    ip_header.set_next_header(TEST_PROTO);
    ip_header.set_hop_limit(4);
    ip_header.set_source(IPV6_SOURCE);
    ip_header.set_destination(IPV6_DESTINATION);
}

fn build_udp_header(packet: &mut [u8], offset: uint) {
    let mut udp_header = MutableUdpHeader::new(packet.slice_from_mut(offset));

    udp_header.set_source(1234); // Arbitary port number
    udp_header.set_destination(1234);
    udp_header.set_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
}

fn build_udp4_packet(packet: &mut [u8], start: uint, msg: &str) {
    build_ipv4_header(packet, start);
    build_udp_header(packet, start + IPV4_HEADER_LEN as uint);

    let data_start = start + IPV4_HEADER_LEN + UDP_HEADER_LEN;
    packet[data_start + 0] = msg.char_at(0) as u8;
    packet[data_start + 1] = msg.char_at(1) as u8;
    packet[data_start + 2] = msg.char_at(2) as u8;
    packet[data_start + 3] = msg.char_at(3) as u8;

    let slice = packet.slice_from_mut(start + IPV4_HEADER_LEN as uint);
    MutableUdpHeader::new(slice).checksum(IPV4_SOURCE, IPV4_DESTINATION, TEST_PROTO);
}

fn build_udp6_packet(packet: &mut [u8], start: uint, msg: &str) {
    build_ipv6_header(packet, start);
    build_udp_header(packet, start + IPV6_HEADER_LEN as uint);

    let data_start = start + IPV6_HEADER_LEN + UDP_HEADER_LEN;
    packet[data_start + 0] = msg.char_at(0) as u8;
    packet[data_start + 1] = msg.char_at(1) as u8;
    packet[data_start + 2] = msg.char_at(2) as u8;
    packet[data_start + 3] = msg.char_at(3) as u8;

    let slice = packet.slice_from_mut(start + IPV6_HEADER_LEN as uint);
    MutableUdpHeader::new(slice).checksum(IPV6_SOURCE, IPV6_DESTINATION, TEST_PROTO);
}

fn get_test_interface() -> NetworkInterface {
    use std::os::getenv;
    use util;

    (*util::get_network_interfaces()
        .as_slice().iter()
        .filter(|x| {
            match getenv("PNET_TEST_IFACE") {
                Some(name) => x.name == name,
                None => x.is_loopback()
            }
        })
        .next()
        .unwrap())
        .clone()
}

// OSes have a nasty habit of tweaking IP fields, so we only check
// the less volatile fields (identification, checksum)
fn check_ipv4_header(packet: &[u8], header: Ipv4Header) {
    let ipv4_header = Ipv4Header::new(packet);

    assert_eq!(header.get_version(), ipv4_header.get_version());
    assert_eq!(header.get_header_length(), ipv4_header.get_header_length());
    assert_eq!(header.get_dscp(), ipv4_header.get_dscp());
    assert_eq!(header.get_ecn(), ipv4_header.get_ecn());
    assert_eq!(header.get_total_length(), ipv4_header.get_total_length());
    assert_eq!(header.get_flags(), ipv4_header.get_flags());
    assert_eq!(header.get_fragment_offset(), ipv4_header.get_fragment_offset());
    assert_eq!(header.get_ttl(), ipv4_header.get_ttl());
    assert_eq!(header.get_next_level_protocol(), ipv4_header.get_next_level_protocol());
    assert_eq!(header.get_source(), ipv4_header.get_source());
    assert_eq!(header.get_destination(), ipv4_header.get_destination());
}

fn layer4(ip: IpAddr, header_len: uint) {
    let mut packet = [0u8, ..IPV6_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];
    let packet_len = header_len + UDP_HEADER_LEN + TEST_DATA_LEN;

    match ip {
        Ipv4Addr(..) => {
            build_udp4_packet(packet.as_mut_slice(), 0, "l4i4")
        },
        Ipv6Addr(..) => {
            build_udp6_packet(packet.as_mut_slice(), 0, "l4i6")
        }
    };

    let udp = UdpHeader::new(packet.slice(header_len, packet_len));

    let (tx, rx) = channel();

    let tc = transport_channel(128, TransportChannelType::Layer4(get_proto(ip)));
    let (mut ttx, mut trx) = match tc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer4: unable to create channel: {}", e),
    };

    let res = try_future( proc() {
        tx.send(());
        pfor!((header, addr) in udp_header_iter(&mut trx) {
            assert_eq!(addr, ip);
            assert_eq!(header, UdpHeader::new(packet.slice(header_len, packet_len)));
            break;
        } on Err(e) {
            panic!("Receive failed for layer4_test(): {}", e);
        })
    });

    rx.recv();
    match ttx.send_to(udp, ip) {
        Ok(res) => assert_eq!(res as uint, UDP_HEADER_LEN + TEST_DATA_LEN),
        Err(e) => panic!("layer4_test failed: {}", e)
    }

    fn get_proto(ip: IpAddr) -> TransportProtocol {
        match ip {
            Ipv4Addr(..) => Ipv4(TEST_PROTO),
            Ipv6Addr(..) => Ipv6(TEST_PROTO)
        }
    }

    match res.unwrap() {
        Err(e) => panic!(e),
        _ => ()
    }
}

#[test]
fn layer4_ipv4() {
    layer4(Ipv4Addr(127, 0, 0, 1), IPV4_HEADER_LEN as uint);
}

#[test]
fn layer4_ipv6() {
    layer4(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), IPV6_HEADER_LEN);
}

#[test]
fn layer3_ipv4() {
    let send_addr = Ipv4Addr(127, 0, 0, 1);
    let mut packet = [0u8, ..IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];

    build_udp4_packet(packet.as_mut_slice(), 0, "l3i4");

    let (tx, rx) = channel();

    let tc =  transport_channel(IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN,
                                    TransportChannelType::Layer3(TEST_PROTO));
    let (mut ttx, mut trx) = match tc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer3: unable to create channel: {}", e),
    };

    let res = try_future( proc() {
        tx.send(());
        pfor!((header, addr) in ipv4_header_iter(&mut trx) {
            assert_eq!(addr, send_addr);
            check_ipv4_header(packet, header);
            let udp_header = UdpHeader::new(header.packet().slice_from(
                                           header.get_header_length() as uint * 4u));
            assert_eq!(udp_header, UdpHeader::new(packet.slice_from(IPV4_HEADER_LEN)));

            assert_eq!(udp_header.packet().slice_from(UDP_HEADER_LEN),
                       packet.slice_from(IPV4_HEADER_LEN + UDP_HEADER_LEN))
            break;
        } on Err(e) {
            panic!("receive failed for layer3_ipv4_test(): {}", e);
        })
    });

    rx.recv();
    match ttx.send_to(Ipv4Header::new(packet), send_addr) {
        Ok(res) => assert_eq!(res as uint, packet.len()),
        Err(e) => panic!("layer3_ipv4_test failed: {}", e)
    }

    match res.unwrap() {
        Err(e) => panic!(e),
        _ => ()
    }

}

#[test]
fn layer2() {
    let interface = get_test_interface();

    let mut packet = [0u8, ..ETHERNET_HEADER_LEN +
                             IPV4_HEADER_LEN +
                             UDP_HEADER_LEN +
                             TEST_DATA_LEN];

    {
        let mut ethernet_header = MutableEthernetHeader::new(packet.as_mut_slice());
        ethernet_header.set_source(interface.mac_address());
        ethernet_header.set_destination(interface.mac_address());
        ethernet_header.set_ethertype(EtherTypes::Ipv4);
    }

    build_udp4_packet(packet.as_mut_slice(), ETHERNET_HEADER_LEN as uint, "l2tt");

    let (tx, rx) = channel();

    let dlc = datalink_channel(&interface,
                               MIN_PACKET_SIZE*2,
                               MIN_PACKET_SIZE*2,
                               DataLinkChannelType::Layer2);
    let (mut dltx, mut dlrx) = match dlc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer2: unable to create channel: {}", e)
    };

    let res = try_future( proc() {
        tx.send(());
        let mut i = 0u;
        pfor!(eh in dlrx.iter() {
            if i == 10_000 {
                panic!("layer2: did not find matching packet after 10_000 iterations");
            }
            if EthernetHeader::new(packet) == eh {
                return;
            }
            i += 1;
        } on Err(e) {
            panic!("layer2 failed: {}", e);
        })
    });

    rx.recv();
    match dltx.send_to(EthernetHeader::new(packet), None) {
        Some(Ok(())) => (),
        Some(Err(e)) => panic!("layer2_test failed: {}", e),
        None => panic!("Provided buffer too small")
    }

    match res.unwrap() {
        Err(e) => panic!(e),
        _ => ()
    }
}

#[test]
fn check_test_environment() {
    use std::os;
    let tasks = os::getenv("RUST_TEST_TASKS");
    if !tasks.is_some() || tasks.unwrap().as_slice() != "1" {
        panic!("Tests must be run with environment variable RUST_TEST_TASKS=1");
    }

    test_iface();

    #[cfg(not(target_os = "linux"))]
    fn test_iface() {
        let iface = os::getenv("PNET_TEST_IFACE");
        if !iface.is_some() {
            panic!("The environment variable PNET_TEST_IFACE must be set.");
        }
    }

    #[cfg(target_os = "linux")]
    fn test_iface() {}
}
<commit_msg>Fix build with latest Rust.<commit_after>// Copyright (c) 2014 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;

use std::clone::Clone;
use std::result::{Ok, Err};
use std::option::{Some};
use std::task::try_future;
use std::io::net::ip::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::iter::Iterator;

use datalink::{datalink_channel, DataLinkChannelType};
use packet::Packet;
use packet::ethernet::{EtherTypes, EthernetHeader, MutableEthernetHeader, EthernetPacket};
use packet::ip::{IpNextHeaderProtocols, IpNextHeaderProtocol};
use packet::ipv4::{Ipv4Header, MutableIpv4Header, Ipv4Packet};
use packet::ipv6::{MutableIpv6Header, Ipv6Packet};
use packet::udp::{UdpHeader, MutableUdpHeader, UdpPacket};
use transport::{udp_header_iter, ipv4_header_iter, transport_channel, TransportProtocol,
                TransportChannelType};
use transport::TransportProtocol::{Ipv4, Ipv6};
use util::NetworkInterface;

const MIN_PACKET_SIZE: uint = 64;
const ETHERNET_HEADER_LEN: uint = 14;
const IPV4_HEADER_LEN: uint = 20;
const IPV6_HEADER_LEN: uint = 40;
const UDP_HEADER_LEN: uint = 8;
const TEST_DATA_LEN: uint = 4;

const IPV4_SOURCE: IpAddr = Ipv4Addr(127, 0, 0, 1);
const IPV4_DESTINATION: IpAddr = Ipv4Addr(127, 0, 0, 1);
const IPV6_SOURCE: IpAddr = Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1);
const IPV6_DESTINATION: IpAddr = Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1);

// Use a protocol which is unlikely to have other packets on
const TEST_PROTO: IpNextHeaderProtocol = IpNextHeaderProtocols::Test1;

fn build_ipv4_header(packet: &mut [u8], offset: uint) {
    let mut ip_header = MutableIpv4Header::new(packet.slice_from_mut(offset));

    let total_len = (IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN) as u16;

    ip_header.set_version(4);
    ip_header.set_header_length(5);
    ip_header.set_total_length(total_len);
    ip_header.set_ttl(4);
    ip_header.set_next_level_protocol(TEST_PROTO);
    ip_header.set_source(IPV4_SOURCE);
    ip_header.set_destination(IPV4_DESTINATION);
    ip_header.checksum();
}

fn build_ipv6_header(packet: &mut [u8], offset: uint) {
    let mut ip_header = MutableIpv6Header::new(packet.slice_from_mut(offset));

    ip_header.set_version(6);
    ip_header.set_payload_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
    ip_header.set_next_header(TEST_PROTO);
    ip_header.set_hop_limit(4);
    ip_header.set_source(IPV6_SOURCE);
    ip_header.set_destination(IPV6_DESTINATION);
}

fn build_udp_header(packet: &mut [u8], offset: uint) {
    let mut udp_header = MutableUdpHeader::new(packet.slice_from_mut(offset));

    udp_header.set_source(1234); // Arbitary port number
    udp_header.set_destination(1234);
    udp_header.set_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
}

fn build_udp4_packet(packet: &mut [u8], start: uint, msg: &str) {
    build_ipv4_header(packet, start);
    build_udp_header(packet, start + IPV4_HEADER_LEN as uint);

    let data_start = start + IPV4_HEADER_LEN + UDP_HEADER_LEN;
    packet[data_start + 0] = msg.char_at(0) as u8;
    packet[data_start + 1] = msg.char_at(1) as u8;
    packet[data_start + 2] = msg.char_at(2) as u8;
    packet[data_start + 3] = msg.char_at(3) as u8;

    let slice = packet.slice_from_mut(start + IPV4_HEADER_LEN as uint);
    MutableUdpHeader::new(slice).checksum(IPV4_SOURCE, IPV4_DESTINATION, TEST_PROTO);
}

fn build_udp6_packet(packet: &mut [u8], start: uint, msg: &str) {
    build_ipv6_header(packet, start);
    build_udp_header(packet, start + IPV6_HEADER_LEN as uint);

    let data_start = start + IPV6_HEADER_LEN + UDP_HEADER_LEN;
    packet[data_start + 0] = msg.char_at(0) as u8;
    packet[data_start + 1] = msg.char_at(1) as u8;
    packet[data_start + 2] = msg.char_at(2) as u8;
    packet[data_start + 3] = msg.char_at(3) as u8;

    let slice = packet.slice_from_mut(start + IPV6_HEADER_LEN as uint);
    MutableUdpHeader::new(slice).checksum(IPV6_SOURCE, IPV6_DESTINATION, TEST_PROTO);
}

fn get_test_interface() -> NetworkInterface {
    use std::os::getenv;
    use util;

    (*util::get_network_interfaces()
        .as_slice().iter()
        .filter(|x| {
            match getenv("PNET_TEST_IFACE") {
                Some(name) => x.name == name,
                None => x.is_loopback()
            }
        })
        .next()
        .unwrap())
        .clone()
}

// OSes have a nasty habit of tweaking IP fields, so we only check
// the less volatile fields (identification, checksum)
fn check_ipv4_header(packet: &[u8], header: Ipv4Header) {
    let ipv4_header = Ipv4Header::new(packet);

    assert_eq!(header.get_version(), ipv4_header.get_version());
    assert_eq!(header.get_header_length(), ipv4_header.get_header_length());
    assert_eq!(header.get_dscp(), ipv4_header.get_dscp());
    assert_eq!(header.get_ecn(), ipv4_header.get_ecn());
    assert_eq!(header.get_total_length(), ipv4_header.get_total_length());
    assert_eq!(header.get_flags(), ipv4_header.get_flags());
    assert_eq!(header.get_fragment_offset(), ipv4_header.get_fragment_offset());
    assert_eq!(header.get_ttl(), ipv4_header.get_ttl());
    assert_eq!(header.get_next_level_protocol(), ipv4_header.get_next_level_protocol());
    assert_eq!(header.get_source(), ipv4_header.get_source());
    assert_eq!(header.get_destination(), ipv4_header.get_destination());
}

fn layer4(ip: IpAddr, header_len: uint) {
    let mut packet = [0u8, ..IPV6_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];
    let packet_len = header_len + UDP_HEADER_LEN + TEST_DATA_LEN;

    match ip {
        Ipv4Addr(..) => {
            build_udp4_packet(packet.as_mut_slice(), 0, "l4i4")
        },
        Ipv6Addr(..) => {
            build_udp6_packet(packet.as_mut_slice(), 0, "l4i6")
        }
    };

    let udp = UdpHeader::new(packet.slice(header_len, packet_len));

    let (tx, rx) = channel();

    let tc = transport_channel(128, TransportChannelType::Layer4(get_proto(ip)));
    let (mut ttx, mut trx) = match tc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer4: unable to create channel: {}", e),
    };

    let res = try_future( proc() {
        tx.send(());
        pfor!((header, addr) in udp_header_iter(&mut trx) {
            assert_eq!(addr, ip);
            assert_eq!(header, UdpHeader::new(packet.slice(header_len, packet_len)));
            break;
        } on Err(e) {
            panic!("Receive failed for layer4_test(): {}", e);
        })
    });

    rx.recv();
    match ttx.send_to(udp, ip) {
        Ok(res) => assert_eq!(res as uint, UDP_HEADER_LEN + TEST_DATA_LEN),
        Err(e) => panic!("layer4_test failed: {}", e)
    }

    fn get_proto(ip: IpAddr) -> TransportProtocol {
        match ip {
            Ipv4Addr(..) => Ipv4(TEST_PROTO),
            Ipv6Addr(..) => Ipv6(TEST_PROTO)
        }
    }

    match res.unwrap() {
        Err(e) => panic!(e),
        _ => ()
    }
}

#[test]
fn layer4_ipv4() {
    layer4(Ipv4Addr(127, 0, 0, 1), IPV4_HEADER_LEN as uint);
}

#[test]
fn layer4_ipv6() {
    layer4(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), IPV6_HEADER_LEN);
}

#[test]
fn layer3_ipv4() {
    let send_addr = Ipv4Addr(127, 0, 0, 1);
    let mut packet = [0u8, ..IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];

    build_udp4_packet(packet.as_mut_slice(), 0, "l3i4");

    let (tx, rx) = channel();

    let tc =  transport_channel(IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN,
                                    TransportChannelType::Layer3(TEST_PROTO));
    let (mut ttx, mut trx) = match tc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer3: unable to create channel: {}", e),
    };

    let res = try_future( proc() {
        tx.send(());
        pfor!((header, addr) in ipv4_header_iter(&mut trx) {
            assert_eq!(addr, send_addr);
            check_ipv4_header(packet.as_slice(), header);
            let udp_header = UdpHeader::new(header.packet().slice_from(
                                           header.get_header_length() as uint * 4u));
            assert_eq!(udp_header, UdpHeader::new(packet.slice_from(IPV4_HEADER_LEN)));

            assert_eq!(udp_header.packet().slice_from(UDP_HEADER_LEN),
                       packet.slice_from(IPV4_HEADER_LEN + UDP_HEADER_LEN))
            break;
        } on Err(e) {
            panic!("receive failed for layer3_ipv4_test(): {}", e);
        })
    });

    rx.recv();
    match ttx.send_to(Ipv4Header::new(packet.as_slice()), send_addr) {
        Ok(res) => assert_eq!(res as uint, packet.len()),
        Err(e) => panic!("layer3_ipv4_test failed: {}", e)
    }

    match res.unwrap() {
        Err(e) => panic!(e),
        _ => ()
    }

}

#[test]
fn layer2() {
    let interface = get_test_interface();

    let mut packet = [0u8, ..ETHERNET_HEADER_LEN +
                             IPV4_HEADER_LEN +
                             UDP_HEADER_LEN +
                             TEST_DATA_LEN];

    {
        let mut ethernet_header = MutableEthernetHeader::new(packet.as_mut_slice());
        ethernet_header.set_source(interface.mac_address());
        ethernet_header.set_destination(interface.mac_address());
        ethernet_header.set_ethertype(EtherTypes::Ipv4);
    }

    build_udp4_packet(packet.as_mut_slice(), ETHERNET_HEADER_LEN as uint, "l2tt");

    let (tx, rx) = channel();

    let dlc = datalink_channel(&interface,
                               MIN_PACKET_SIZE*2,
                               MIN_PACKET_SIZE*2,
                               DataLinkChannelType::Layer2);
    let (mut dltx, mut dlrx) = match dlc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer2: unable to create channel: {}", e)
    };

    let res = try_future( proc() {
        tx.send(());
        let mut i = 0u;
        pfor!(eh in dlrx.iter() {
            if i == 10_000 {
                panic!("layer2: did not find matching packet after 10_000 iterations");
            }
            if EthernetHeader::new(packet.as_slice()) == eh {
                return;
            }
            i += 1;
        } on Err(e) {
            panic!("layer2 failed: {}", e);
        })
    });

    rx.recv();
    match dltx.send_to(EthernetHeader::new(packet.as_slice()), None) {
        Some(Ok(())) => (),
        Some(Err(e)) => panic!("layer2_test failed: {}", e),
        None => panic!("Provided buffer too small")
    }

    match res.unwrap() {
        Err(e) => panic!(e),
        _ => ()
    }
}

#[test]
fn check_test_environment() {
    use std::os;
    let tasks = os::getenv("RUST_TEST_TASKS");
    if !tasks.is_some() || tasks.unwrap().as_slice() != "1" {
        panic!("Tests must be run with environment variable RUST_TEST_TASKS=1");
    }

    test_iface();

    #[cfg(not(target_os = "linux"))]
    fn test_iface() {
        let iface = os::getenv("PNET_TEST_IFACE");
        if !iface.is_some() {
            panic!("The environment variable PNET_TEST_IFACE must be set.");
        }
    }

    #[cfg(target_os = "linux")]
    fn test_iface() {}
}
<|endoftext|>