<commit_before>/*!

LZ4 Decompression

This module contains an implementation in Rust of decompression of LZ4-encoded
streams. This is exposed as a standard `Reader` interface wrapping an
underlying `Reader` interface. In this manner, the decompressor can be viewed
as a stream. It maintains its own internal buffer, but the size of this is
constant as the stream is read.

# Example

```rust
let stream = Path::new("path/to/file.lz4").open_reader(io::Open);
let decompressed = l4z::Decoder::new(stream).read_to_end();
```

# Credit

This implementation is largely based on Branimir Karadžić's implementation which
can be found at https://github.com/bkaradzic/go-lz4.

*/

use std::rt::io;
use std::rt::io::extensions::{ReaderUtil, ReaderByteConversions};
use std::vec;
use std::num;

#[deriving(Eq)]
enum State {
    StreamStart,
    BlockStart,
    BlockEnd,

    // raw blocks of uncompressed data
    RawBlock(u32),

    // blocks of compressed data (no headers or checksums)
    DataStart,
    NeedsConsumption(u8, uint),
    DecompressStart(u8),
    Decompress(uint),

    EndOfStream,
    Cleanup,
}

enum ConsumptionResult {
    KeepGoing(uint),
    Consumed(uint),
    SomethingBad,
}

static BUF_SIZE: uint = 128 << 10;
static FLUSH_SIZE: uint = 1 << 16;
static MAGIC: u32 = 0x184d2204;

/// A decoder of an underlying lz4 stream. This is the decompressor used to
/// inflate an underlying stream.
pub struct Decoder<R> {
    /// This is the stream which is owned by this decoder. It may be moved out
    /// of, but the operation would also invalidate the `Decoder` instance.
    r: R,

    priv state: State,
    priv end: uint,
    priv start: uint,
    priv buf: ~[u8],
    priv remaining_bytes: uint,

    // metadata from the stream descriptor
    priv blk_checksum: bool,
    priv stream_checksum: bool,
}

impl<R: io::Reader + io::Seek> Decoder<R> {
    /// Creates a new decoder which will decompress the LZ4-encoded stream
    /// which will be read from `r`. This decoder will consume ownership of the
    /// reader, but it is accessible via the `r` field on the object returned.
    pub fn new(r: R) -> Decoder<R> {
        Decoder {
            r: r,
            state: StreamStart,
            start: 0,
            end: 0,
            buf: vec::from_elem(BUF_SIZE, 0u8),
            blk_checksum: false,
            stream_checksum: false,
            remaining_bytes: 0,
        }
    }

    fn inner_byte(&mut self) -> Option<u8> {
        match self.r.read_byte() {
            Some(b) => {
                self.remaining_bytes -= 1;
                Some(b)
            }
            None => None,
        }
    }

    fn inner_u16(&mut self) -> u16 {
        self.remaining_bytes -= 2;
        self.r.read_le_u16_()
    }

    fn inner_read(&mut self, dst: &mut [u8]) -> Option<uint> {
        match self.r.read(dst) {
            Some(n) => {
                self.remaining_bytes -= n;
                Some(n)
            }
            None => None,
        }
    }

    fn read_len(&mut self, code: u8) -> Option<uint> {
        let mut ret = code as uint;
        assert!(code < 16);
        if code == 0xf {
            loop {
                match self.inner_byte() {
                    None => return None,
                    Some(255) => ret += 255,
                    Some(n) => {
                        ret += n as uint;
                        break
                    }
                }
            }
        }
        return Some(ret);
    }

    fn consume(&mut self, len: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        let buf = unsafe {
            use std::cast;
            cast::transmute_copy(&self.buf.mut_slice(self.end, self.end + len))
        };
        match self.inner_read(buf) {
            Some(n) => {
                self.end += n;
                Consumed(in_output)
            }
            None => SomethingBad
        }
    }

    fn flush(&mut self, len: uint, dst: &mut [u8]) -> uint {
        if self.end + len > self.buf.len() {
            assert!(self.start > FLUSH_SIZE);
            let s = self.start - FLUSH_SIZE;

            // Copy bytes out into the destination
            let fill = num::min(dst.len(), s);
            info!("flushing out {}", fill);
            vec::bytes::copy_memory(dst, self.buf, fill);

            // Slide all the bytes back down in the internal buffer
            for i in range(0, fill) {
                self.buf[i] = self.buf[fill + i];
            }

            self.end -= fill;
            self.start -= fill;
            return fill;
        }
        return 0;
    }

    fn cp(&mut self, len: uint, decr: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        for i in range(0, len) {
            self.buf[self.end + i] = self.buf[self.start + i];
        }

        self.end += len;
        self.start += len - decr;
        return Consumed(in_output);
    }

    fn exec(&mut self, mut state: State, buf: &mut [u8]) -> Option<uint> {
        let mut offset = 0;
        loop {
            debug!("state: {:?} {:x} {:x} at {}", state, self.start, self.end,
                   self.r.tell());
            match state {
                StreamStart => {
                    match self.header() {
                        Some(()) => {}
                        None => return None,
                    }
                    state = BlockStart;
                }

                BlockStart => {
                    state = match self.r.read_le_u32_() {
                        0 => EndOfStream,
                        n if n & 0x80000000 != 0 => RawBlock(n & 0x7fffffff),
                        n => {
                            self.remaining_bytes = n as uint;
                            info!("bytes left {}", self.remaining_bytes);
                            DataStart
                        }
                    }
                }

                DataStart => {
                    //info!("remaining: {}", self.remaining_bytes as int);
                    assert!(self.remaining_bytes as int > 0);
                    let code = match self.inner_byte() {
                        Some(i) => i,
                        None => return None,
                    };
                    debug!("at: {:x} {} {:x}", self.r.tell(),
                           self.remaining_bytes as int, code);

                    // XXX: I/O errors
                    let len = match self.read_len(code >> 4) {
                        Some(l) => l, None => return None,
                    };
                    state = NeedsConsumption(code, len);
                }

                NeedsConsumption(code, len) => {
                    match self.consume(len, buf.mut_slice_from(offset)) {
                        // XXX: I/O error
                        SomethingBad => return None,
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            if self.remaining_bytes == 0 {
                                state = BlockStart;
                            } else {
                                state = DecompressStart(code);
                            }
                        }
                    }
                }

                DecompressStart(code) => {
                    // XXX: I/O errors
                    let back = self.inner_u16();
                    debug!("got back: {}", back);
                    self.start = self.end - back as uint;

                    let len = match self.read_len(code & 0xf) {
                        Some(l) => l, None => return None,
                    };

                    state = Decompress(len);
                }

                Decompress(len) => {
                    let literal = self.end - self.start;
                    if literal < 4 {
                        static DECR: [uint, ..4] = [0, 3, 2, 3];
                        match self.cp(4, DECR[literal],
                                      buf.mut_slice_from(offset)) {
                            SomethingBad => unreachable!(),
                            KeepGoing(amt_written) => {
                                self.state = state;
                                return Some(amt_written + offset);
                            }
                            Consumed(amt_written) => offset += amt_written,
                        }
                    } else {
                        len += 4;
                    }
                    match self.cp(len, 0, buf.mut_slice_from(offset)) {
                        SomethingBad => unreachable!(),
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            state = DataStart;
                        }
                    }
                }

                RawBlock(remaining) => {
                    let dst = buf.mut_slice_from(offset);
                    if dst.len() == 0 {
                        self.state = state;
                        return Some(offset);
                    }
                    if remaining == 0 {
                        state = BlockEnd;
                        continue
                    }
                    let amt = num::min(remaining as uint, dst.len());
                    match self.r.read(dst.mut_slice(offset, offset + amt)) {
                        Some(n) => {
                            offset += n;
                            state = RawBlock(remaining - n as u32);
                        }
                        None => return None
                    }
                }

                BlockEnd => {
                    // XXX: implement checksums
                    if self.blk_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring block cksum: {:?}", cksum);
                    }
                    state = BlockStart;
                }

                EndOfStream => {
                    // XXX: implement checksums
                    if self.stream_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring stream checksum: {:?}", cksum);
                    }
                    state = Cleanup;
                    self.start = 0;
                }

                Cleanup => {
                    let dst = buf.mut_slice_from(offset);
                    let src = self.buf.slice(self.start, self.end);
                    let amt = num::min(dst.len(), src.len());
                    if amt == 0 { return None }
                    vec::bytes::copy_memory(dst, src, amt);
                    self.start += amt;
                    self.state = Cleanup;
                    return Some(amt + offset);
                }
            }
        }
    }

    fn header(&mut self) -> Option<()> {
        // Make sure the magic number is what's expected.
        if self.r.read_le_u32_() != MAGIC { return None }

        let flg = match self.r.read_byte() { Some(i) => i, None => return None };
        let bd = match self.r.read_byte() { Some(i) => i, None => return None };

        // bits 7/6, the version number. Right now this must be 1
        if (flg >> 6) != 0b01 { return None }
        // bit 5 is the "block independence", don't care about this yet
        // bit 4 is whether blocks have checksums or not
        self.blk_checksum = (flg & 0x10) != 0;
        // bit 3 is whether there is a following stream size
        let stream_size = (flg & 0x08) != 0;
        // bit 2 is whether there is a stream checksum
        self.stream_checksum = (flg & 0x04) != 0;
        // bit 1 is reserved
        // bit 0 is whether there is a preset dictionary
        let preset_dictionary = (flg & 0x01) != 0;

        static MAX_SIZES: [uint, ..8] =
            [0, 0, 0, 0, // all N/A
             64 << 10,   // 64KB
             256 << 10,  // 256 KB
             1 << 20,    // 1MB
             4 << 20];   // 4MB

        // bit 7 is reserved
        // bits 6-4 are the maximum block size
        let max_block_size = MAX_SIZES[(bd >> 4) & 0x7];
        // bits 3-0 are reserved

        // read off other portions of the stream
        let size = if stream_size {Some(self.r.read_le_u64_())} else {None};
        assert!(!preset_dictionary, "preset dictionaries not supported yet");

        debug!("blk: {}", self.blk_checksum);
        debug!("stream: {}", self.stream_checksum);
        debug!("max size: {}", max_block_size);
        debug!("stream size: {:?}", size);

        // XXX: implement checksums
        let cksum = self.r.read_byte();
        debug!("ignoring header checksum: {:?}", cksum);
        return Some(());
    }
}

impl<R: io::Reader + io::Seek> io::Reader for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        info!("reading {}", buf.len());
        let out = self.exec(self.state, buf);
        info!("got {:?}", out);
        out
    }

    fn eof(&mut self) -> bool { false }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::rt::io::extensions::ReaderUtil;
    use std::rt::io::mem::BufReader;
    use std::rt::io::Reader;
    use std::rand;

    fn test_decode(input: &[u8], output: &[u8]) {
        let mut d = Decoder::new(BufReader::new(input));

        let got = d.read_to_end();
        assert!(got.as_slice() == output);
    }

    #[test]
    fn decode() {
        let reference = include_bin!("data/test.txt");
        test_decode(include_bin!("data/test.lz4.1"), reference);
        test_decode(include_bin!("data/test.lz4.2"), reference);
        test_decode(include_bin!("data/test.lz4.3"), reference);
        test_decode(include_bin!("data/test.lz4.4"), reference);
        test_decode(include_bin!("data/test.lz4.5"), reference);
        test_decode(include_bin!("data/test.lz4.6"), reference);
        test_decode(include_bin!("data/test.lz4.7"), reference);
        test_decode(include_bin!("data/test.lz4.8"), reference);
        test_decode(include_bin!("data/test.lz4.9"), reference);
    }

    #[test]
    fn one_byte_at_a_time() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        loop {
            match d.read_byte() {
                Some(b) => out.push(b),
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }

    #[test]
    fn random_byte_lengths() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        let mut buf = [0u8, ..40];
        loop {
            match d.read(buf.mut_slice_to(1 + rand::random::<uint>() % 40)) {
                Some(n) => {
                    out.push_all(buf.slice_to(n));
                }
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }
}
<commit_msg>Implement eof<commit_after>/*!

LZ4 Decompression

This module contains an implementation in Rust of decompression of LZ4-encoded
streams. This is exposed as a standard `Reader` interface wrapping an
underlying `Reader` interface. In this manner, the decompressor can be viewed
as a stream. It maintains its own internal buffer, but the size of this is
constant as the stream is read.

# Example

```rust
let stream = Path::new("path/to/file.lz4").open_reader(io::Open);
let decompressed = l4z::Decoder::new(stream).read_to_end();
```

# Credit

This implementation is largely based on Branimir Karadžić's implementation which
can be found at https://github.com/bkaradzic/go-lz4.

*/

use std::rt::io;
use std::rt::io::extensions::{ReaderUtil, ReaderByteConversions};
use std::vec;
use std::num;

#[deriving(Eq)]
enum State {
    StreamStart,
    BlockStart,
    BlockEnd,

    // raw blocks of uncompressed data
    RawBlock(u32),

    // blocks of compressed data (no headers or checksums)
    DataStart,
    NeedsConsumption(u8, uint),
    DecompressStart(u8),
    Decompress(uint),

    EndOfStream,
    Cleanup,
}

enum ConsumptionResult {
    KeepGoing(uint),
    Consumed(uint),
    SomethingBad,
}

static BUF_SIZE: uint = 128 << 10;
static FLUSH_SIZE: uint = 1 << 16;
static MAGIC: u32 = 0x184d2204;

/// A decoder of an underlying lz4 stream. This is the decompressor used to
/// inflate an underlying stream.
pub struct Decoder<R> {
    /// This is the stream which is owned by this decoder. It may be moved out
    /// of, but the operation would also invalidate the `Decoder` instance.
    r: R,

    priv state: State,
    priv end: uint,
    priv start: uint,
    priv buf: ~[u8],
    priv remaining_bytes: uint,

    // metadata from the stream descriptor
    priv blk_checksum: bool,
    priv stream_checksum: bool,
}

impl<R: io::Reader + io::Seek> Decoder<R> {
    /// Creates a new decoder which will decompress the LZ4-encoded stream
    /// which will be read from `r`. This decoder will consume ownership of the
    /// reader, but it is accessible via the `r` field on the object returned.
    pub fn new(r: R) -> Decoder<R> {
        Decoder {
            r: r,
            state: StreamStart,
            start: 0,
            end: 0,
            buf: vec::from_elem(BUF_SIZE, 0u8),
            blk_checksum: false,
            stream_checksum: false,
            remaining_bytes: 0,
        }
    }

    fn inner_byte(&mut self) -> Option<u8> {
        match self.r.read_byte() {
            Some(b) => {
                self.remaining_bytes -= 1;
                Some(b)
            }
            None => None,
        }
    }

    fn inner_u16(&mut self) -> u16 {
        self.remaining_bytes -= 2;
        self.r.read_le_u16_()
    }

    fn inner_read(&mut self, dst: &mut [u8]) -> Option<uint> {
        match self.r.read(dst) {
            Some(n) => {
                self.remaining_bytes -= n;
                Some(n)
            }
            None => None,
        }
    }

    fn read_len(&mut self, code: u8) -> Option<uint> {
        let mut ret = code as uint;
        assert!(code < 16);
        if code == 0xf {
            loop {
                match self.inner_byte() {
                    None => return None,
                    Some(255) => ret += 255,
                    Some(n) => {
                        ret += n as uint;
                        break
                    }
                }
            }
        }
        return Some(ret);
    }

    fn consume(&mut self, len: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        let buf = unsafe {
            use std::cast;
            cast::transmute_copy(&self.buf.mut_slice(self.end, self.end + len))
        };
        match self.inner_read(buf) {
            Some(n) => {
                self.end += n;
                Consumed(in_output)
            }
            None => SomethingBad
        }
    }

    fn flush(&mut self, len: uint, dst: &mut [u8]) -> uint {
        if self.end + len > self.buf.len() {
            assert!(self.start > FLUSH_SIZE);
            let s = self.start - FLUSH_SIZE;

            // Copy bytes out into the destination
            let fill = num::min(dst.len(), s);
            info!("flushing out {}", fill);
            vec::bytes::copy_memory(dst, self.buf, fill);

            // Slide all the bytes back down in the internal buffer
            for i in range(0, fill) {
                self.buf[i] = self.buf[fill + i];
            }

            self.end -= fill;
            self.start -= fill;
            return fill;
        }
        return 0;
    }

    fn cp(&mut self, len: uint, decr: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        for i in range(0, len) {
            self.buf[self.end + i] = self.buf[self.start + i];
        }

        self.end += len;
        self.start += len - decr;
        return Consumed(in_output);
    }

    fn exec(&mut self, mut state: State, buf: &mut [u8]) -> Option<uint> {
        let mut offset = 0;
        loop {
            debug!("state: {:?} {:x} {:x} at {}", state, self.start, self.end,
                   self.r.tell());
            match state {
                StreamStart => {
                    match self.header() {
                        Some(()) => {}
                        None => return None,
                    }
                    state = BlockStart;
                }

                BlockStart => {
                    state = match self.r.read_le_u32_() {
                        0 => EndOfStream,
                        n if n & 0x80000000 != 0 => RawBlock(n & 0x7fffffff),
                        n => {
                            self.remaining_bytes = n as uint;
                            info!("bytes left {}", self.remaining_bytes);
                            DataStart
                        }
                    }
                }

                DataStart => {
                    //info!("remaining: {}", self.remaining_bytes as int);
                    assert!(self.remaining_bytes as int > 0);
                    let code = match self.inner_byte() {
                        Some(i) => i,
                        None => return None,
                    };
                    debug!("at: {:x} {} {:x}", self.r.tell(),
                           self.remaining_bytes as int, code);

                    // XXX: I/O errors
                    let len = match self.read_len(code >> 4) {
                        Some(l) => l, None => return None,
                    };
                    state = NeedsConsumption(code, len);
                }

                NeedsConsumption(code, len) => {
                    match self.consume(len, buf.mut_slice_from(offset)) {
                        // XXX: I/O error
                        SomethingBad => return None,
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            if self.remaining_bytes == 0 {
                                state = BlockStart;
                            } else {
                                state = DecompressStart(code);
                            }
                        }
                    }
                }

                DecompressStart(code) => {
                    // XXX: I/O errors
                    let back = self.inner_u16();
                    debug!("got back: {}", back);
                    self.start = self.end - back as uint;

                    let len = match self.read_len(code & 0xf) {
                        Some(l) => l, None => return None,
                    };

                    state = Decompress(len);
                }

                Decompress(len) => {
                    let literal = self.end - self.start;
                    if literal < 4 {
                        static DECR: [uint, ..4] = [0, 3, 2, 3];
                        match self.cp(4, DECR[literal],
                                      buf.mut_slice_from(offset)) {
                            SomethingBad => unreachable!(),
                            KeepGoing(amt_written) => {
                                self.state = state;
                                return Some(amt_written + offset);
                            }
                            Consumed(amt_written) => offset += amt_written,
                        }
                    } else {
                        len += 4;
                    }
                    match self.cp(len, 0, buf.mut_slice_from(offset)) {
                        SomethingBad => unreachable!(),
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            state = DataStart;
                        }
                    }
                }

                RawBlock(remaining) => {
                    let dst = buf.mut_slice_from(offset);
                    if dst.len() == 0 {
                        self.state = state;
                        return Some(offset);
                    }
                    if remaining == 0 {
                        state = BlockEnd;
                        continue
                    }
                    let amt = num::min(remaining as uint, dst.len());
                    match self.r.read(dst.mut_slice(offset, offset + amt)) {
                        Some(n) => {
                            offset += n;
                            state = RawBlock(remaining - n as u32);
                        }
                        None => return None
                    }
                }

                BlockEnd => {
                    // XXX: implement checksums
                    if self.blk_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring block cksum: {:?}", cksum);
                    }
                    state = BlockStart;
                }

                EndOfStream => {
                    // XXX: implement checksums
                    if self.stream_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring stream checksum: {:?}", cksum);
                    }
                    state = Cleanup;
                    self.start = 0;
                }

                Cleanup => {
                    let dst = buf.mut_slice_from(offset);
                    let src = self.buf.slice(self.start, self.end);
                    let amt = num::min(dst.len(), src.len());
                    if amt == 0 { return None }
                    vec::bytes::copy_memory(dst, src, amt);
                    self.start += amt;
                    self.state = Cleanup;
                    return Some(amt + offset);
                }
            }
        }
    }

    fn header(&mut self) -> Option<()> {
        // Make sure the magic number is what's expected.
        if self.r.read_le_u32_() != MAGIC { return None }

        let flg = match self.r.read_byte() { Some(i) => i, None => return None };
        let bd = match self.r.read_byte() { Some(i) => i, None => return None };

        // bits 7/6, the version number. Right now this must be 1
        if (flg >> 6) != 0b01 { return None }
        // bit 5 is the "block independence", don't care about this yet
        // bit 4 is whether blocks have checksums or not
        self.blk_checksum = (flg & 0x10) != 0;
        // bit 3 is whether there is a following stream size
        let stream_size = (flg & 0x08) != 0;
        // bit 2 is whether there is a stream checksum
        self.stream_checksum = (flg & 0x04) != 0;
        // bit 1 is reserved
        // bit 0 is whether there is a preset dictionary
        let preset_dictionary = (flg & 0x01) != 0;

        static MAX_SIZES: [uint, ..8] =
            [0, 0, 0, 0, // all N/A
             64 << 10,   // 64KB
             256 << 10,  // 256 KB
             1 << 20,    // 1MB
             4 << 20];   // 4MB

        // bit 7 is reserved
        // bits 6-4 are the maximum block size
        let max_block_size = MAX_SIZES[(bd >> 4) & 0x7];
        // bits 3-0 are reserved

        // read off other portions of the stream
        let size = if stream_size {Some(self.r.read_le_u64_())} else {None};
        assert!(!preset_dictionary, "preset dictionaries not supported yet");

        debug!("blk: {}", self.blk_checksum);
        debug!("stream: {}", self.stream_checksum);
        debug!("max size: {}", max_block_size);
        debug!("stream size: {:?}", size);

        // XXX: implement checksums
        let cksum = self.r.read_byte();
        debug!("ignoring header checksum: {:?}", cksum);
        return Some(());
    }
}

impl<R: io::Reader + io::Seek> io::Reader for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        info!("reading {}", buf.len());
        let out = self.exec(self.state, buf);
        info!("got {:?}", out);
        out
    }

    fn eof(&mut self) -> bool {
        self.state == Cleanup && self.start == self.end
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::rt::io::extensions::ReaderUtil;
    use std::rt::io::mem::BufReader;
    use std::rt::io::Reader;
    use std::rand;

    fn test_decode(input: &[u8], output: &[u8]) {
        let mut d = Decoder::new(BufReader::new(input));

        let got = d.read_to_end();
        assert!(got.as_slice() == output);
    }

    #[test]
    fn decode() {
        let reference = include_bin!("data/test.txt");
        test_decode(include_bin!("data/test.lz4.1"), reference);
        test_decode(include_bin!("data/test.lz4.2"), reference);
        test_decode(include_bin!("data/test.lz4.3"), reference);
        test_decode(include_bin!("data/test.lz4.4"), reference);
        test_decode(include_bin!("data/test.lz4.5"), reference);
        test_decode(include_bin!("data/test.lz4.6"), reference);
        test_decode(include_bin!("data/test.lz4.7"), reference);
        test_decode(include_bin!("data/test.lz4.8"), reference);
        test_decode(include_bin!("data/test.lz4.9"), reference);
    }

    #[test]
    fn one_byte_at_a_time() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        assert!(!d.eof());
        let mut out = ~[];
        loop {
            match d.read_byte() {
                Some(b) => out.push(b),
                None => break
            }
        }
        assert!(d.eof());
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }

    #[test]
    fn random_byte_lengths() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        let mut buf = [0u8, ..40];
        loop {
            match d.read(buf.mut_slice_to(1 + rand::random::<uint>() % 40)) {
                Some(n) => {
                    out.push_all(buf.slice_to(n));
                }
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }
}
<|endoftext|>


<commit_before>// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::io;

use remain::sorted;
use thiserror::Error as ThisError;

#[cfg_attr(windows, path = "sys/windows/tube.rs")]
#[cfg_attr(not(windows), path = "sys/unix/tube.rs")]
mod tube;
use std::time::Duration;

use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde::Serialize;
pub use tube::*;

impl Tube {
    /// Creates a Send/Recv pair of Tubes.
    pub fn directional_pair() -> Result<(SendTube, RecvTube)> {
        let (t1, t2) = Self::pair()?;
        Ok((SendTube(t1), RecvTube(t2)))
    }
}

use crate::AsRawDescriptor;
use crate::ReadNotifier;

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only send messages. Cloneable.
pub struct SendTube(Tube);

#[allow(dead_code)]
impl SendTube {
    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_send_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    pub fn send<T: Serialize>(&self, msg: &T) -> Result<()> {
        self.0.send(msg)
    }

    pub fn try_clone(&self) -> Result<Self> {
        Ok(SendTube(
            #[allow(deprecated)]
            self.0.try_clone()?,
        ))
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only recv messages.
pub struct RecvTube(Tube);

#[allow(dead_code)]
impl RecvTube {
    pub fn recv<T: DeserializeOwned>(&self) -> Result<T> {
        self.0.recv()
    }

    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_recv_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

impl ReadNotifier for RecvTube {
    fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
        self.0.get_read_notifier()
    }
}

#[sorted]
#[derive(ThisError, Debug)]
pub enum Error {
    #[cfg(windows)]
    #[error("attempt to duplicate descriptor via broker failed")]
    BrokerDupDescriptor,
    #[error("failed to clone transport: {0}")]
    Clone(io::Error),
    #[error("tube was disconnected")]
    Disconnected,
    #[error("failed to duplicate descriptor: {0}")]
    DupDescriptor(io::Error),
    #[cfg(windows)]
    #[error("failed to flush named pipe: {0}")]
    Flush(io::Error),
    #[cfg(unix)]
    #[error("byte framing mode is not supported")]
    InvalidFramingMode,
    #[error("failed to serialize/deserialize json from packet: {0}")]
    Json(serde_json::Error),
    #[error("cancelled a queued async operation")]
    OperationCancelled,
    #[error("failed to crate tube pair: {0}")]
    Pair(io::Error),
    #[cfg(windows)]
    #[error("encountered protobuf error: {0}")]
    Proto(protobuf::ProtobufError),
    #[error("failed to receive packet: {0}")]
    Recv(io::Error),
    #[error("Received a message with a zero sized body. This should not happen.")]
    RecvUnexpectedEmptyBody,
    #[error("failed to send packet: {0}")]
    Send(crate::platform::Error),
    #[error("failed to send packet: {0}")]
    SendIo(io::Error),
    #[error("failed to write packet to intermediate buffer: {0}")]
    SendIoBuf(io::Error),
    #[error("attempted to send too many file descriptors")]
    SendTooManyFds,
    #[error("failed to set recv timeout: {0}")]
    SetRecvTimeout(io::Error),
    #[error("failed to set send timeout: {0}")]
    SetSendTimeout(io::Error),
}

pub type Result<T> = std::result::Result<T, Error>;
<commit_msg>base: tube: allow to clone sending part of the tube<commit_after>// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::io;

use remain::sorted;
use thiserror::Error as ThisError;

#[cfg_attr(windows, path = "sys/windows/tube.rs")]
#[cfg_attr(not(windows), path = "sys/unix/tube.rs")]
mod tube;
use std::time::Duration;

use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde::Serialize;
pub use tube::*;

impl Tube {
    /// Creates a Send/Recv pair of Tubes.
    pub fn directional_pair() -> Result<(SendTube, RecvTube)> {
        let (t1, t2) = Self::pair()?;
        Ok((SendTube(t1), RecvTube(t2)))
    }

    pub fn try_clone_send_tube(&self) -> Result<SendTube> {
        // Safe because receiving is only allowed on original Tube.
        #[allow(deprecated)]
        let send_end = self.try_clone()?;
        Ok(SendTube(send_end))
    }
}

use crate::AsRawDescriptor;
use crate::ReadNotifier;

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only send messages. Cloneable.
pub struct SendTube(Tube);

#[allow(dead_code)]
impl SendTube {
    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_send_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    pub fn send<T: Serialize>(&self, msg: &T) -> Result<()> {
        self.0.send(msg)
    }

    pub fn try_clone(&self) -> Result<Self> {
        Ok(SendTube(
            #[allow(deprecated)]
            self.0.try_clone()?,
        ))
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only recv messages.
pub struct RecvTube(Tube);

#[allow(dead_code)]
impl RecvTube {
    pub fn recv<T: DeserializeOwned>(&self) -> Result<T> {
        self.0.recv()
    }

    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_recv_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

impl ReadNotifier for RecvTube {
    fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
        self.0.get_read_notifier()
    }
}

#[sorted]
#[derive(ThisError, Debug)]
pub enum Error {
    #[cfg(windows)]
    #[error("attempt to duplicate descriptor via broker failed")]
    BrokerDupDescriptor,
    #[error("failed to clone transport: {0}")]
    Clone(io::Error),
    #[error("tube was disconnected")]
    Disconnected,
    #[error("failed to duplicate descriptor: {0}")]
    DupDescriptor(io::Error),
    #[cfg(windows)]
    #[error("failed to flush named pipe: {0}")]
    Flush(io::Error),
    #[cfg(unix)]
    #[error("byte framing mode is not supported")]
    InvalidFramingMode,
    #[error("failed to serialize/deserialize json from packet: {0}")]
    Json(serde_json::Error),
    #[error("cancelled a queued async operation")]
    OperationCancelled,
    #[error("failed to crate tube pair: {0}")]
    Pair(io::Error),
    #[cfg(windows)]
    #[error("encountered protobuf error: {0}")]
    Proto(protobuf::ProtobufError),
    #[error("failed to receive packet: {0}")]
    Recv(io::Error),
    #[error("Received a message with a zero sized body. This should not happen.")]
    RecvUnexpectedEmptyBody,
    #[error("failed to send packet: {0}")]
    Send(crate::platform::Error),
    #[error("failed to send packet: {0}")]
    SendIo(io::Error),
    #[error("failed to write packet to intermediate buffer: {0}")]
    SendIoBuf(io::Error),
    #[error("attempted to send too many file descriptors")]
    SendTooManyFds,
    #[error("failed to set recv timeout: {0}")]
    SetRecvTimeout(io::Error),
    #[error("failed to set send timeout: {0}")]
    SetSendTimeout(io::Error),
}

pub type Result<T> = std::result::Result<T, Error>;
<|endoftext|>


<commit_before>#![feature(test)]

extern crate abomonation;
extern crate test;

use abomonation::*;
use test::Bencher;
use std::io::Read;

#[bench] fn empty_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![(); 1024]); }
#[bench] fn empty_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![(); 1024]); }

#[bench] fn u64_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![0u64; 1024]); }
#[bench] fn u64_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![0u64; 1024]); }

#[bench] fn u8_u64_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn u8_u64_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![(0u8, 0u64); 512]); }

#[bench] fn string10_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string10_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!"); 1024]); }

#[bench] fn string20_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn string20_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }

#[bench] fn vec_u_s_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

#[bench] fn vec_u_vn_s_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_vn_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

fn _bench_enc<T: Abomonation>(bencher: &mut Bencher, record: T) {

    // prepare encoded data for bencher.bytes
    let mut bytes = Vec::new();
    unsafe { encode(&record, &mut bytes); }

    // repeatedly encode this many bytes
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        bytes.clear();
        encode(&record, &mut bytes);
    });
}

fn _bench_dec<T: Abomonation+Eq>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    unsafe { encode(&record, &mut bytes); }

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        unsafe { decode::<T>(&mut bytes) }.is_some()
        // assert!(&record == result);
    });
}
<commit_msg>added unsafe<commit_after>#![feature(test)]

extern crate abomonation;
extern crate test;

use abomonation::*;
use test::Bencher;
use std::io::Read;

#[bench] fn empty_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![(); 1024]); }
#[bench] fn empty_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![(); 1024]); }

#[bench] fn u64_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![0u64; 1024]); }
#[bench] fn u64_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![0u64; 1024]); }

#[bench] fn u8_u64_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn u8_u64_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![(0u8, 0u64); 512]); }

#[bench] fn string10_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string10_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!"); 1024]); }

#[bench] fn string20_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn string20_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }

#[bench] fn vec_u_s_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

#[bench] fn vec_u_vn_s_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_vn_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

fn _bench_enc<T: Abomonation>(bencher: &mut Bencher, record: T) {

    // prepare encoded data for bencher.bytes
    let mut bytes = Vec::new();
    unsafe { encode(&record, &mut bytes); }

    // repeatedly encode this many bytes
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        bytes.clear();
        unsafe { encode(&record, &mut bytes) }
    });
}

fn _bench_dec<T: Abomonation+Eq>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    unsafe { encode(&record, &mut bytes); }

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        unsafe { decode::<T>(&mut bytes) }.is_some()
        // assert!(&record == result);
    });
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::trie::*;
use adapton::engine::*;

// The code that we want to compare/measure under naive versus DCG engines:
fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
    v.into_iter().fold(t, |acc, i| TrieIntro::extend(name_of_usize(i), acc, i))
}

fn bench_naive_add_dups() {
    init_naive();
    let mut naive_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    let mut ones = vec![1;100];
    let mut twos = vec![2;100];
    let mut threes = vec![3;100];
    twos.append(&mut threes);
    ones.append(&mut twos);
    for i in ones.iter() {
        v.push(*i);
        naive_input = doit(v.clone(), naive_input.clone())
    }
}

fn bench_dcg_add_dups() {
    init_dcg();
    let mut dcg_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    let mut ones = vec![1;100];
    let mut twos = vec![2;100];
    let mut threes = vec![3;100];
    twos.append(&mut threes);
    ones.append(&mut twos);
    for i in ones.iter() {
        v.push(*i);
        dcg_input = doit(v.clone(), dcg_input.clone())
    }
}

#[bench]
fn benchmark_naive_trie(b: &mut Bencher) {
    b.iter(|| bench_naive_add_dups())
}

#[bench]
fn benchmark_dcg_trie(b: &mut Bencher) {
    b.iter(|| bench_dcg_add_dups())
}
<commit_msg>Wrap extensions in benchmark in articulation point for fast comparison<commit_after>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::trie::*;
use adapton::engine::*;

// The code that we want to compare/measure under naive versus DCG engines:
fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
    v.into_iter().fold(t, |acc, i| {
        let t = Trie::art(cell(name_of_usize(i), acc));
        let t = Trie::name(name_of_usize(i), t);
        Trie::extend(name_of_usize(i), t, i)
    })
}

fn bench_naive_add_dups() {
    init_naive();
    let mut naive_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    let mut ones = vec![1;100];
    let mut twos = vec![2;100];
    let mut threes = vec![3;100];
    twos.append(&mut threes);
    ones.append(&mut twos);
    for i in ones.iter() {
        v.push(*i);
        naive_input = doit(v.clone(), naive_input.clone())
    }
}

fn bench_dcg_add_dups() {
    init_dcg();
    let mut dcg_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    let mut ones = vec![1;100];
    let mut twos = vec![2;100];
    let mut threes = vec![3;100];
    twos.append(&mut threes);
    ones.append(&mut twos);
    for i in ones.iter() {
        v.push(*i);
        dcg_input = doit(v.clone(), dcg_input.clone())
    }
}

#[bench]
fn benchmark_naive_trie(b: &mut Bencher) {
    b.iter(|| bench_naive_add_dups())
}

#[bench]
fn benchmark_dcg_trie(b: &mut Bencher) {
    b.iter(|| bench_dcg_add_dups())
}
<|endoftext|>


<commit_before>extern crate crossbeam;
extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;

extern crate libimagrt;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;
use clap::{Arg, App, AppSettings, SubCommand};

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;

fn help(cmds: Vec<String>) {
    println!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:
    "#);

    for cmd in cmds.iter() {
        println!("\t{}", cmd);
    }

    println!(r#"

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#);
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    get_commands()
        .iter()
        .fold(app, |app, cmd| app.subcommand(SubCommand::with_name(cmd)))
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .arg(Arg::with_name("help")
             .long("help")
             .short("h")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Show help"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .settings(&[AppSettings::AllowExternalSubcommands])
}

fn main() {
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let rt       = generate_runtime_setup(appname, version, about, build_ui);
    let matches  = rt.cli();

    debug!("matches: {:?}", matches);
    if matches.is_present("help") {
        debug!("Calling help()");
        help(get_commands());
        exit(0);
    }

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        let mut result = vec![];
        for command in get_commands().iter() {
            result.push(crossbeam::scope(|scope| {
                scope.spawn(|| {
                    let v = Command::new(command).arg("--version").output();
                    match v {
                        Ok(v) => match String::from_utf8(v.stdout) {
                            Ok(s) => format!("{} -> {}", command, s),
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        },
                        Err(e) => format!("Failed calling {} -> {:?}", command, e),
                    }
                })
            }))
        }

        for versionstring in result.into_iter().map(|handle| handle.join()) {
            println!("{}", versionstring);
        }
    }

    matches.subcommand_name()
        .map(|subcommand| {

            let mut subcommand_args = vec![];

            for arg in Runtime::arg_names() {
                matches.value_of(arg)
                    .map(|value| {
                        subcommand_args.push(arg);
                        subcommand_args.push(value);
                    });
            }

            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .arg(subcommand)
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(42));
                    }
                },

                Err(e) => {
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1337);
                        }
                    }
                }
            }
        });
}
<commit_msg>We do not need to pass the subcommand as arg here<commit_after>extern crate crossbeam;
extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;

extern crate libimagrt;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;
use clap::{Arg, App, AppSettings, SubCommand};

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;

fn help(cmds: Vec<String>) {
    println!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:
    "#);

    for cmd in cmds.iter() {
        println!("\t{}", cmd);
    }

    println!(r#"

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#);
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    get_commands()
        .iter()
        .fold(app, |app, cmd| app.subcommand(SubCommand::with_name(cmd)))
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .arg(Arg::with_name("help")
             .long("help")
             .short("h")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Show help"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .settings(&[AppSettings::AllowExternalSubcommands])
}

fn main() {
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let rt       = generate_runtime_setup(appname, version, about, build_ui);
    let matches  = rt.cli();

    debug!("matches: {:?}", matches);
    if matches.is_present("help") {
        debug!("Calling help()");
        help(get_commands());
        exit(0);
    }

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        let mut result = vec![];
        for command in get_commands().iter() {
            result.push(crossbeam::scope(|scope| {
                scope.spawn(|| {
                    let v = Command::new(command).arg("--version").output();
                    match v {
                        Ok(v) => match String::from_utf8(v.stdout) {
                            Ok(s) => format!("{} -> {}", command, s),
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        },
                        Err(e) => format!("Failed calling {} -> {:?}", command, e),
                    }
                })
            }))
        }

        for versionstring in result.into_iter().map(|handle| handle.join()) {
            println!("{}", versionstring);
        }
    }

    matches.subcommand_name()
        .map(|subcommand| {

            let mut subcommand_args = vec![];

            for arg in Runtime::arg_names() {
                matches.value_of(arg)
                    .map(|value| {
                        subcommand_args.push(arg);
                        subcommand_args.push(value);
                    });
            }

            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(42));
                    }
                },

                Err(e) => {
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1337);
                        }
                    }
                }
            }
        });
}
<|endoftext|>


<commit_before>#![cfg_attr(test, allow(dead_code))]

use self::imp::{drop_handler, make_handler};

pub use self::imp::cleanup;
pub use self::imp::init;

pub struct Handler {
    _data: *mut libc::c_void,
}

impl Handler {
    pub unsafe fn new() -> Handler {
        make_handler()
    }

    fn null() -> Handler {
        Handler { _data: crate::ptr::null_mut() }
    }
}

impl Drop for Handler {
    fn drop(&mut self) {
        unsafe {
            drop_handler(self);
        }
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "macos",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "solaris",
    all(target_os = "netbsd", not(target_vendor = "rumprun")),
    target_os = "openbsd"
))]
mod imp {
    use super::Handler;
    use crate::mem;
    use crate::ptr;

    use libc::MAP_FAILED;
    use libc::{mmap, munmap};
    use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
    use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
    use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};

    use crate::sys::unix::os::page_size;
    use crate::sys_common::thread_info;

    #[cfg(any(target_os = "linux", target_os = "android"))]
    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
        #[repr(C)]
        struct siginfo_t {
            a: [libc::c_int; 3], // si_signo, si_errno, si_code
            si_addr: *mut libc::c_void,
        }

        (*(info as *const siginfo_t)).si_addr as usize
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
        (*info).si_addr as usize
    }

    // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
    // (unmapped pages) at the end of every thread's stack, so if a thread ends
    // up running into the guard page it'll trigger this handler. We want to
    // detect these cases and print out a helpful error saying that the stack
    // has overflowed. All other signals, however, should go back to what they
    // were originally supposed to do.
    //
    // This handler currently exists purely to print an informative message
    // whenever a thread overflows its stack. We then abort to exit and
    // indicate a crash, but to avoid a misleading SIGSEGV that might lead
    // users to believe that unsafe code has accessed an invalid pointer; the
    // SIGSEGV encountered when overflowing the stack is expected and
    // well-defined.
    //
    // If this is not a stack overflow, the handler un-registers itself and
    // then returns (to allow the original signal to be delivered again).
    // Returning from this kind of signal handler is technically not defined
    // to work when reading the POSIX spec strictly, but in practice it turns
    // out many large systems and all implementations allow returning from a
    // signal handler to work. For a more detailed explanation see the
    // comments on #26458.
    unsafe extern "C" fn signal_handler(
        signum: libc::c_int,
        info: *mut libc::siginfo_t,
        _data: *mut libc::c_void,
    ) {
        use crate::sys_common::util::report_overflow;

        let guard = thread_info::stack_guard().unwrap_or(0..0);
        let addr = siginfo_si_addr(info);

        // If the faulting address is within the guard page, then we print a
        // message saying so and abort.
        if guard.start <= addr && addr < guard.end {
            report_overflow();
            rtabort!("stack overflow");
        } else {
            // Unregister ourselves by reverting back to the default behavior.
            let mut action: sigaction = mem::zeroed();
            action.sa_sigaction = SIG_DFL;
            sigaction(signum, &action, ptr::null_mut());

            // See comment above for why this function returns.
        }
    }

    static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
    static mut NEED_ALTSTACK: bool = false;

    pub unsafe fn init() {
        let mut action: sigaction = mem::zeroed();
        for &signal in &[SIGSEGV, SIGBUS] {
            sigaction(signal, ptr::null_mut(), &mut action);
            // Configure our signal handler if one is not already set.
            if action.sa_sigaction == SIG_DFL {
                action.sa_flags = SA_SIGINFO | SA_ONSTACK;
                action.sa_sigaction = signal_handler as sighandler_t;
                sigaction(signal, &action, ptr::null_mut());
                NEED_ALTSTACK = true;
            }
        }

        let handler = make_handler();
        MAIN_ALTSTACK = handler._data;
        mem::forget(handler);
    }

    pub unsafe fn cleanup() {
        Handler { _data: MAIN_ALTSTACK };
    }

    unsafe fn get_stackp() -> *mut libc::c_void {
        let stackp =
            mmap(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
        if stackp == MAP_FAILED {
            panic!("failed to allocate an alternative stack");
        }
        let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
        if guard_result != 0 {
            panic!("failed to set up alternative stack guard page");
        }
        stackp.add(page_size())
    }

    #[cfg(any(
        target_os = "linux",
        target_os = "macos",
        target_os = "freebsd",
        target_os = "netbsd",
        target_os = "openbsd",
        target_os = "solaris"
    ))]
    unsafe fn get_stack() -> libc::stack_t {
        libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
    }

    #[cfg(target_os = "dragonfly")]
    unsafe fn get_stack() -> libc::stack_t {
        libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
    }

    pub unsafe fn make_handler() -> Handler {
        if !NEED_ALTSTACK {
            return Handler::null();
        }
        let mut stack = mem::zeroed();
        sigaltstack(ptr::null(), &mut stack);
        // Configure alternate signal stack, if one is not already set.
        if stack.ss_flags & SS_DISABLE != 0 {
            stack = get_stack();
            sigaltstack(&stack, ptr::null_mut());
            Handler { _data: stack.ss_sp as *mut libc::c_void }
        } else {
            Handler::null()
        }
    }

    pub unsafe fn drop_handler(handler: &mut Handler) {
        if !handler._data.is_null() {
            let stack = libc::stack_t {
                ss_sp: ptr::null_mut(),
                ss_flags: SS_DISABLE,
                // Workaround for bug in macOS implementation of sigaltstack
                // UNIX2003 which returns ENOMEM when disabling a stack while
                // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
                // both ss_sp and ss_size should be ignored in this case.
                ss_size: SIGSTKSZ,
            };
            sigaltstack(&stack, ptr::null_mut());
            // We know from `get_stackp` that the alternate stack we installed is part of a mapping
            // that started one page earlier, so walk back a page and unmap from there.
            munmap(handler._data.sub(page_size()), SIGSTKSZ + page_size());
        }
    }
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "macos",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "solaris",
    all(target_os = "netbsd", not(target_vendor = "rumprun")),
    target_os = "openbsd"
)))]
mod imp {
    pub unsafe fn init() {}

    pub unsafe fn cleanup() {}

    pub unsafe fn make_handler() -> super::Handler {
        super::Handler::null()
    }

    pub unsafe fn drop_handler(_handler: &mut super::Handler) {}
}
<commit_msg>fix formatting<commit_after>#![cfg_attr(test, allow(dead_code))]

use self::imp::{drop_handler, make_handler};

pub use self::imp::cleanup;
pub use self::imp::init;

pub struct Handler {
    _data: *mut libc::c_void,
}

impl Handler {
    pub unsafe fn new() -> Handler {
        make_handler()
    }

    fn null() -> Handler {
        Handler { _data: crate::ptr::null_mut() }
    }
}

impl Drop for Handler {
    fn drop(&mut self) {
        unsafe {
            drop_handler(self);
        }
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "macos",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "solaris",
    all(target_os = "netbsd", not(target_vendor = "rumprun")),
    target_os = "openbsd"
))]
mod imp {
    use super::Handler;
    use crate::mem;
    use crate::ptr;

    use libc::MAP_FAILED;
    use libc::{mmap, munmap};
    use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
    use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
    use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};

    use crate::sys::unix::os::page_size;
    use crate::sys_common::thread_info;

    #[cfg(any(target_os = "linux", target_os = "android"))]
    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
        #[repr(C)]
        struct siginfo_t {
            a: [libc::c_int; 3], // si_signo, si_errno, si_code
            si_addr: *mut libc::c_void,
        }

        (*(info as *const siginfo_t)).si_addr as usize
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
        (*info).si_addr as usize
    }

    // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
    // (unmapped pages) at the end of every thread's stack, so if a thread ends
    // up running into the guard page it'll trigger this handler. We want to
    // detect these cases and print out a helpful error saying that the stack
    // has overflowed. All other signals, however, should go back to what they
    // were originally supposed to do.
    //
    // This handler currently exists purely to print an informative message
    // whenever a thread overflows its stack. We then abort to exit and
    // indicate a crash, but to avoid a misleading SIGSEGV that might lead
    // users to believe that unsafe code has accessed an invalid pointer; the
    // SIGSEGV encountered when overflowing the stack is expected and
    // well-defined.
    //
    // If this is not a stack overflow, the handler un-registers itself and
    // then returns (to allow the original signal to be delivered again).
    // Returning from this kind of signal handler is technically not defined
    // to work when reading the POSIX spec strictly, but in practice it turns
    // out many large systems and all implementations allow returning from a
    // signal handler to work. For a more detailed explanation see the
    // comments on #26458.
    unsafe extern "C" fn signal_handler(
        signum: libc::c_int,
        info: *mut libc::siginfo_t,
        _data: *mut libc::c_void,
    ) {
        use crate::sys_common::util::report_overflow;

        let guard = thread_info::stack_guard().unwrap_or(0..0);
        let addr = siginfo_si_addr(info);

        // If the faulting address is within the guard page, then we print a
        // message saying so and abort.
        if guard.start <= addr && addr < guard.end {
            report_overflow();
            rtabort!("stack overflow");
        } else {
            // Unregister ourselves by reverting back to the default behavior.
            let mut action: sigaction = mem::zeroed();
            action.sa_sigaction = SIG_DFL;
            sigaction(signum, &action, ptr::null_mut());

            // See comment above for why this function returns.
        }
    }

    static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
    static mut NEED_ALTSTACK: bool = false;

    pub unsafe fn init() {
        let mut action: sigaction = mem::zeroed();
        for &signal in &[SIGSEGV, SIGBUS] {
            sigaction(signal, ptr::null_mut(), &mut action);
            // Configure our signal handler if one is not already set.
            if action.sa_sigaction == SIG_DFL {
                action.sa_flags = SA_SIGINFO | SA_ONSTACK;
                action.sa_sigaction = signal_handler as sighandler_t;
                sigaction(signal, &action, ptr::null_mut());
                NEED_ALTSTACK = true;
            }
        }

        let handler = make_handler();
        MAIN_ALTSTACK = handler._data;
        mem::forget(handler);
    }

    pub unsafe fn cleanup() {
        Handler { _data: MAIN_ALTSTACK };
    }

    unsafe fn get_stackp() -> *mut libc::c_void {
        let stackp = mmap(
            ptr::null_mut(),
            SIGSTKSZ + page_size(),
            PROT_READ | PROT_WRITE,
            MAP_PRIVATE | MAP_ANON,
            -1,
            0,
        );
        if stackp == MAP_FAILED {
            panic!("failed to allocate an alternative stack");
        }
        let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
        if guard_result != 0 {
            panic!("failed to set up alternative stack guard page");
        }
        stackp.add(page_size())
    }

    #[cfg(any(
        target_os = "linux",
        target_os = "macos",
        target_os = "freebsd",
        target_os = "netbsd",
        target_os = "openbsd",
        target_os = "solaris"
    ))]
    unsafe fn get_stack() -> libc::stack_t {
        libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
    }

    #[cfg(target_os = "dragonfly")]
    unsafe fn get_stack() -> libc::stack_t {
        libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
    }

    pub unsafe fn make_handler() -> Handler {
        if !NEED_ALTSTACK {
            return Handler::null();
        }
        let mut stack = mem::zeroed();
        sigaltstack(ptr::null(), &mut stack);
        // Configure alternate signal stack, if one is not already set.
        if stack.ss_flags & SS_DISABLE != 0 {
            stack = get_stack();
            sigaltstack(&stack, ptr::null_mut());
            Handler { _data: stack.ss_sp as *mut libc::c_void }
        } else {
            Handler::null()
        }
    }

    pub unsafe fn drop_handler(handler: &mut Handler) {
        if !handler._data.is_null() {
            let stack = libc::stack_t {
                ss_sp: ptr::null_mut(),
                ss_flags: SS_DISABLE,
                // Workaround for bug in macOS implementation of sigaltstack
                // UNIX2003 which returns ENOMEM when disabling a stack while
                // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
                // both ss_sp and ss_size should be ignored in this case.
                ss_size: SIGSTKSZ,
            };
            sigaltstack(&stack, ptr::null_mut());
            // We know from `get_stackp` that the alternate stack we installed is part of a mapping
            // that started one page earlier, so walk back a page and unmap from there.
            munmap(handler._data.sub(page_size()), SIGSTKSZ + page_size());
        }
    }
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "macos",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "solaris",
    all(target_os = "netbsd", not(target_vendor = "rumprun")),
    target_os = "openbsd"
)))]
mod imp {
    pub unsafe fn init() {}

    pub unsafe fn cleanup() {}

    pub unsafe fn make_handler() -> super::Handler {
        super::Handler::null()
    }

    pub unsafe fn drop_handler(_handler: &mut super::Handler) {}
}
<|endoftext|>


<commit_before>use spin::Mutex;

use crate::memory::Frame;
use crate::paging::{ActivePageTable, Page, PageFlags, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::paging::mapper::PageFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::mode_info::VBEModeInfo;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod mode_info;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable) {
    println!("Starting graphical debug");

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info_addr = 0x5200 + crate::PHYS_OFFSET;
        let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) };
        width = mode_info.xresolution as usize;
        height = mode_info.yresolution as usize;
        physbaseptr = mode_info.physbaseptr as usize;
    }

    println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr);

    {
        let size = width * height;

        let onscreen = physbaseptr + crate::PHYS_OFFSET;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().data() - crate::PHYS_OFFSET));
                let flags = PageFlags::new().write(true).custom_flag(EntryFlags::HUGE_PAGE.bits(), true);
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush();
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    if let Some(debug_display) = DEBUG_DISPLAY.lock().take() {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.width * display.height;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush();
        }
    }

    println!("Finished graphical debug");
}
<commit_msg>workaround for crash on graphical debug finish<commit_after>use spin::Mutex;

use crate::memory::Frame;
use crate::paging::{ActivePageTable, Page, PageFlags, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::paging::mapper::PageFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::mode_info::VBEModeInfo;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod mode_info;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable) {
    println!("Starting graphical debug");

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info_addr = 0x5200 + crate::PHYS_OFFSET;
        let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) };
        width = mode_info.xresolution as usize;
        height = mode_info.yresolution as usize;
        physbaseptr = mode_info.physbaseptr as usize;
    }

    println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr);

    {
        let size = width * height;

        let onscreen = physbaseptr + crate::PHYS_OFFSET;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().data() - crate::PHYS_OFFSET));
                let flags = PageFlags::new().write(true).custom_flag(EntryFlags::HUGE_PAGE.bits(), true);
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush();
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    let debug_display_opt = DEBUG_DISPLAY.lock().take();
    if let Some(debug_display) = debug_display_opt {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.onscreen.len() * 4;
        //TODO: fix crash if we unmap this memory
        if false {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush();
        }
    }

    println!("Finished graphical debug");
}
<|endoftext|>


<commit_before>extern crate spirv_cross;
use spirv_cross::{hlsl, spirv};

mod common;
use common::words_from_bytes;

#[test]
fn ast_gets_entry_points() {
    let entry_points = spirv::Ast::<hlsl::Target>::parse(&spirv::Module::from_words(
        words_from_bytes(include_bytes!("shaders/simple.spv")),
    )).unwrap()
        .get_entry_points()
        .unwrap();

    assert_eq!(entry_points.len(), 1);
    assert_eq!(entry_points[0].name, "main");
}
<commit_msg>Add a resource test<commit_after>extern crate spirv_cross;
use spirv_cross::{hlsl, spirv};

mod common;
use common::words_from_bytes;

#[test]
fn ast_gets_entry_points() {
    let entry_points = spirv::Ast::<hlsl::Target>::parse(&spirv::Module::from_words(
        words_from_bytes(include_bytes!("shaders/simple.spv")),
    )).unwrap()
        .get_entry_points()
        .unwrap();

    assert_eq!(entry_points.len(), 1);
    assert_eq!(entry_points[0].name, "main");
}

#[test]
fn ast_gets_shader_resources() {
    let shader_resources = spirv::Ast::<hlsl::Target>::parse(&spirv::Module::from_words(
        words_from_bytes(include_bytes!("shaders/simple.spv")),
    )).unwrap()
        .get_shader_resources()
        .unwrap();

    let spirv::ShaderResources {
        uniform_buffers,
        stage_inputs,
        stage_outputs,
        ..
    } = shader_resources;

    assert_eq!(uniform_buffers.len(), 1);
    assert_eq!(uniform_buffers[0].name, "uniform_buffer_object");
    assert_eq!(shader_resources.storage_buffers.len(), 0);
    assert_eq!(stage_inputs.len(), 2);
    assert!(
        stage_inputs
            .iter()
            .any(|stage_input| { stage_input.name == "a_normal" })
    );
    assert!(
        stage_inputs
            .iter()
            .any(|stage_input| { stage_input.name == "a_position" })
    );
    assert_eq!(stage_outputs.len(), 1);
    assert!(
        stage_outputs
            .iter()
            .any(|stage_output| { stage_output.name == "v_normal" })
    );
    assert_eq!(shader_resources.subpass_inputs.len(), 0);
    assert_eq!(shader_resources.storage_images.len(), 0);
    assert_eq!(shader_resources.sampled_images.len(), 0);
    assert_eq!(shader_resources.atomic_counters.len(), 0);
    assert_eq!(shader_resources.push_constant_buffers.len(), 0);
    assert_eq!(shader_resources.separate_images.len(), 0);
    assert_eq!(shader_resources.separate_samplers.len(), 0);
}
<|endoftext|>