<commit_before>#![feature(test)]
#![feature(asm)]
#![feature(core)]

use std::str::pattern::{Pattern,Searcher,SearchStep};

#[derive(Debug,Copy,Clone)]
pub struct ByteSearch {
    pub needle: u64,
    pub count: u8,
}

impl ByteSearch {
    pub fn new() -> ByteSearch {
        ByteSearch { needle: 0, count: 0 }
    }

    pub fn push(&mut self, byte: u8) {
        assert!(self.count < 8);
        self.needle <<= 8;
        self.needle |= byte as u64;
        self.count += 1;
    }

    #[inline]
    pub fn find(self, haystack: &str) -> Option<usize> {
        let haystack = haystack.as_bytes();

        let ptr = haystack.as_ptr();
        let mut offset = 0;
        let mut len = haystack.len();

        let mut res: usize;

        // Zero-length strings have a pointer set to 0x1. Even though the
        // length is zero, we still trigger a bad access exception. I
        // think this indicates that the instruction reads in 16 bytes
        // worth of memory at a time, regardless of the length instruction.
        //
        // This could also be an indication of a subtle bug, where we
        // might trigger access violations if we are near the end of a
        // page. See the comment by Renat Saifutdinov on
        // http://www.strchr.com/strcmp_and_strlen_using_sse_4.2
        // It is suggested to use an "aligned read with mask false bits"
        // to avoid the problem.
        //
        // We don't do this yet.
        if len == 0 { return None }

        loop {
            unsafe {
                asm!("pcmpestri $$0, ($1, $5), $2"
                     : // output operands
                     "={ecx}"(res)
                     : // input operands
                     "r"(ptr),
                     "x"(self.needle),
                     "{rdx}"(len),
                     "{rax}"(self.count),
                     "r"(offset)
                     : // clobbers
                     : // options
                     );
            }

            // We know if it matched if the zero flag is set (or
            // unset?), we shouldn't need to test res...
            if res == 16 {
                if len <= 16 {
                    return None;
                }

                offset += 16;
                len -= 16;
            } else {
                return Some(res + offset);
            }
        }
    }
}

impl<'a> Pattern<'a> for ByteSearch {
    type Searcher = ByteSearcher<'a>;

    fn into_searcher(self, haystack: &'a str) -> ByteSearcher<'a> {
        ByteSearcher { haystack: haystack, offset: 0, needle: self }
    }
}

#[derive(Debug,Copy,Clone)]
pub struct ByteSearcher<'a> {
    haystack: &'a str,
    offset: usize,
    needle: ByteSearch,
}

unsafe impl<'a> Searcher<'a> for ByteSearcher<'a> {
    fn haystack(&self) -> &'a str { self.haystack }

    #[inline]
    fn next(&mut self) -> SearchStep {
        if self.offset >= self.haystack.len() { return SearchStep::Done }

        let left_to_search = &self.haystack[self.offset..]; // TODO: unchecked_slice?
        let idx = self.needle.find(left_to_search);

        // If there's no match, then the rest of the string should be
        // returned.
        let idx = idx.unwrap_or(self.haystack.len());

        let (res, next_offset) = if idx == 0 {
            // A match occurs at the beginning of the string
            let next = self.offset + 1;
            (SearchStep::Match(self.offset, next), next)
        } else {
            // A match occurs somewhere further in the string
            let next = self.offset + idx;
            (SearchStep::Reject(self.offset, next), next)
        };

        self.offset = next_offset;
        res
    }
}

#[cfg(test)]
mod test {
    extern crate quickcheck;

    use super::ByteSearch;
    use self::quickcheck::quickcheck;
    use std::str::pattern::{Pattern,Searcher,SearchStep};

    pub const SPACE: ByteSearch       = ByteSearch { needle: 0x0000000000000020, count: 1 };
    // < > &
    pub const XML_DELIM_3: ByteSearch = ByteSearch { needle: 0x00000000003c3e26, count: 3 };
    // < > & ' "
    pub const XML_DELIM_5: ByteSearch = ByteSearch { needle: 0x0000003c3e262722, count: 5 };

    #[test]
    fn works_as_find_does_for_single_characters() {
        fn prop(s: String) -> bool {
            s.find(SPACE) == s.find(' ')
        }
        quickcheck(prop as fn(String) -> bool);
    }

    #[test]
    fn works_as_find_does_for_multiple_characters() {
        fn prop(s: String) -> bool {
            s.find(XML_DELIM_5) == s.find(&['<', '>', '&', '\'', '"'][..])
        }
        quickcheck(prop as fn(String) -> bool);
    }

    #[test]
    fn pattern_does_not_backtrack_after_first() {
        let mut searcher = SPACE.into_searcher("hello w ");
        assert_eq!(SearchStep::Reject(0,5), searcher.next());
        assert_eq!(SearchStep::Match(5,6), searcher.next());
        assert_eq!(SearchStep::Reject(6,7), searcher.next());
        assert_eq!(SearchStep::Match(7,8), searcher.next());
        assert_eq!(SearchStep::Done, searcher.next());
    }

    #[test]
    fn space_is_found() {
        // Since the algorithm operates on 16-byte chunks, it's
        // important to cover tests around that boundary. Since 16
        // isn't that big of a number, we might as well do all of
        // them.

        assert_eq!(Some(0),  SPACE.find(" "));
        assert_eq!(Some(1),  SPACE.find("0 "));
        assert_eq!(Some(2),  SPACE.find("01 "));
        assert_eq!(Some(3),  SPACE.find("012 "));
        assert_eq!(Some(4),  SPACE.find("0123 "));
        assert_eq!(Some(5),  SPACE.find("01234 "));
        assert_eq!(Some(6),  SPACE.find("012345 "));
        assert_eq!(Some(7),  SPACE.find("0123456 "));
        assert_eq!(Some(8),  SPACE.find("01234567 "));
        assert_eq!(Some(9),  SPACE.find("012345678 "));
        assert_eq!(Some(10), SPACE.find("0123456789 "));
        assert_eq!(Some(11), SPACE.find("0123456789A "));
        assert_eq!(Some(12), SPACE.find("0123456789AB "));
        assert_eq!(Some(13), SPACE.find("0123456789ABC "));
        assert_eq!(Some(14), SPACE.find("0123456789ABCD "));
        assert_eq!(Some(15), SPACE.find("0123456789ABCDE "));
        assert_eq!(Some(16), SPACE.find("0123456789ABCDEF "));
        assert_eq!(Some(17), SPACE.find("0123456789ABCDEFG "));
    }

    #[test]
    fn space_not_found() {
        // Since the algorithm operates on 16-byte chunks, it's
        // important to cover tests around that boundary. Since 16
        // isn't that big of a number, we might as well do all of
        // them.

        assert_eq!(None, SPACE.find(""));
        assert_eq!(None, SPACE.find("0"));
        assert_eq!(None, SPACE.find("01"));
        assert_eq!(None, SPACE.find("012"));
        assert_eq!(None, SPACE.find("0123"));
        assert_eq!(None, SPACE.find("01234"));
        assert_eq!(None, SPACE.find("012345"));
        assert_eq!(None, SPACE.find("0123456"));
        assert_eq!(None, SPACE.find("01234567"));
        assert_eq!(None, SPACE.find("012345678"));
        assert_eq!(None, SPACE.find("0123456789"));
        assert_eq!(None, SPACE.find("0123456789A"));
        assert_eq!(None, SPACE.find("0123456789AB"));
        assert_eq!(None, SPACE.find("0123456789ABC"));
        assert_eq!(None, SPACE.find("0123456789ABCD"));
        assert_eq!(None, SPACE.find("0123456789ABCDE"));
        assert_eq!(None, SPACE.find("0123456789ABCDEF"));
        assert_eq!(None, SPACE.find("0123456789ABCDEFG"));
    }

    #[test]
    fn xml_delim_3_is_found() {
        assert_eq!(Some(0), XML_DELIM_3.find("<"));
        assert_eq!(Some(0), XML_DELIM_3.find(">"));
        assert_eq!(Some(0), XML_DELIM_3.find("&"));
        assert_eq!(None,    XML_DELIM_3.find(""));
    }

    #[test]
    fn xml_delim_5_is_found() {
        assert_eq!(Some(0), XML_DELIM_5.find("<"));
        assert_eq!(Some(0), XML_DELIM_5.find(">"));
        assert_eq!(Some(0), XML_DELIM_5.find("&"));
        assert_eq!(Some(0), XML_DELIM_5.find("'"));
        assert_eq!(Some(0), XML_DELIM_5.find("\""));
        assert_eq!(None,    XML_DELIM_5.find(""));
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use super::test::{SPACE,XML_DELIM_3,XML_DELIM_5};
    use std::iter;

    fn prefix_string() -> String {
        iter::repeat("a").take(5 * 1024 * 1024).collect()
    }

    fn bench_space<F>(b: &mut test::Bencher, f: F)
        where F: Fn(&str) -> Option<usize>
    {
        let mut haystack = prefix_string();
        haystack.push(' ');

        b.iter(|| test::black_box(f(&haystack)));
        b.bytes = haystack.len() as u64;
    }

    #[bench]
    fn space_assembly(b: &mut test::Bencher) {
        bench_space(b, |hs| SPACE.find(hs))
    }

    #[bench]
    fn space_assembly_as_pattern(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(SPACE))
    }

    #[bench]
    fn space_find_string(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(" "))
    }

    #[bench]
    fn space_find_char(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(' '))
    }

    #[bench]
    fn space_find_char_set(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(&[' '][..]))
    }

    #[bench]
    fn space_find_byte(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.as_bytes().iter().position(|&v| v == b' '))
    }

    fn bench_xml_delim_3<F>(b: &mut test::Bencher, f: F)
        where F: Fn(&str) -> Option<usize>
    {
        let mut haystack = prefix_string();
        haystack.push('&');

        b.iter(|| test::black_box(f(&haystack)));
        b.bytes = haystack.len() as u64;
    }

    #[bench]
    fn xml_delim_3_assembly(b: &mut test::Bencher) {
        bench_xml_delim_3(b, |hs| XML_DELIM_3.find(hs))
    }

    #[bench]
    fn xml_delim_3_assembly_as_pattern(b: &mut test::Bencher) {
        bench_xml_delim_3(b, |hs| hs.find(XML_DELIM_3))
    }

    #[bench]
    fn xml_delim_3_find_char_set(b: &mut test::Bencher) {
        bench_xml_delim_3(b, |hs| hs.find(&['<', '>', '&'][..]))
    }

    fn bench_xml_delim_5<F>(b: &mut test::Bencher, f: F)
        where F: Fn(&str) -> Option<usize>
    {
        let mut haystack = prefix_string();
        haystack.push('"');

        b.iter(|| test::black_box(f(&haystack)));
        b.bytes = haystack.len() as u64;
    }

    #[bench]
    fn xml_delim_5_assembly(b: &mut test::Bencher) {
        bench_xml_delim_5(b, |hs| XML_DELIM_5.find(hs))
    }

    #[bench]
    fn xml_delim_5_assembly_as_pattern(b: &mut test::Bencher) {
        bench_xml_delim_5(b, |hs| hs.find(XML_DELIM_5))
    }

    #[bench]
    fn xml_delim_5_find_char_set(b: &mut test::Bencher) {
        bench_xml_delim_5(b, |hs| hs.find(&['<', '>', '&', '\'', '"'][..]))
    }
}
<commit_msg>Quickcheck generates ASCII alphanumeric strings only<commit_after>#![feature(test)]
#![feature(asm)]
#![feature(core)]

use std::str::pattern::{Pattern,Searcher,SearchStep};

#[derive(Debug,Copy,Clone)]
pub struct ByteSearch {
    pub needle: u64,
    pub count: u8,
}

impl ByteSearch {
    pub fn new() -> ByteSearch {
        ByteSearch { needle: 0, count: 0 }
    }

    pub fn push(&mut self, byte: u8) {
        assert!(self.count < 8);
        self.needle <<= 8;
        self.needle |= byte as u64;
        self.count += 1;
    }

    #[inline]
    pub fn find(self, haystack: &str) -> Option<usize> {
        let haystack = haystack.as_bytes();

        let ptr = haystack.as_ptr();
        let mut offset = 0;
        let mut len = haystack.len();

        let mut res: usize;

        // Zero-length strings have a pointer set to 0x1. Even though the
        // length is zero, we still trigger a bad access exception. I
        // think this indicates that the instruction reads in 16 bytes
        // worth of memory at a time, regardless of the length instruction.
        //
        // This could also be an indication of a subtle bug, where we
        // might trigger access violations if we are near the end of a
        // page. See the comment by Renat Saifutdinov on
        // http://www.strchr.com/strcmp_and_strlen_using_sse_4.2
        // It is suggested to use an "aligned read with mask false bits"
        // to avoid the problem.
        //
        // We don't do this yet.
        if len == 0 { return None }

        loop {
            unsafe {
                asm!("pcmpestri $$0, ($1, $5), $2"
                     : // output operands
                     "={ecx}"(res)
                     : // input operands
                     "r"(ptr),
                     "x"(self.needle),
                     "{rdx}"(len),
                     "{rax}"(self.count),
                     "r"(offset)
                     : // clobbers
                     : // options
                     );
            }

            // We know if it matched if the zero flag is set (or
            // unset?), we shouldn't need to test res...
            if res == 16 {
                if len <= 16 {
                    return None;
                }

                offset += 16;
                len -= 16;
            } else {
                return Some(res + offset);
            }
        }
    }
}

impl<'a> Pattern<'a> for ByteSearch {
    type Searcher = ByteSearcher<'a>;

    fn into_searcher(self, haystack: &'a str) -> ByteSearcher<'a> {
        ByteSearcher { haystack: haystack, offset: 0, needle: self }
    }
}

#[derive(Debug,Copy,Clone)]
pub struct ByteSearcher<'a> {
    haystack: &'a str,
    offset: usize,
    needle: ByteSearch,
}

unsafe impl<'a> Searcher<'a> for ByteSearcher<'a> {
    fn haystack(&self) -> &'a str { self.haystack }

    #[inline]
    fn next(&mut self) -> SearchStep {
        if self.offset >= self.haystack.len() { return SearchStep::Done }

        let left_to_search = &self.haystack[self.offset..]; // TODO: unchecked_slice?
        let idx = self.needle.find(left_to_search);

        // If there's no match, then the rest of the string should be
        // returned.
        let idx = idx.unwrap_or(self.haystack.len());

        let (res, next_offset) = if idx == 0 {
            // A match occurs at the beginning of the string
            let next = self.offset + 1;
            (SearchStep::Match(self.offset, next), next)
        } else {
            // A match occurs somewhere further in the string
            let next = self.offset + idx;
            (SearchStep::Reject(self.offset, next), next)
        };

        self.offset = next_offset;
        res
    }
}

#[cfg(test)]
mod test {
    extern crate quickcheck;

    use super::ByteSearch;
    use self::quickcheck::quickcheck;
    use std::str::pattern::{Pattern,Searcher,SearchStep};

    pub const SPACE: ByteSearch       = ByteSearch { needle: 0x0000000000000020, count: 1 };
    // a
    pub const VOWEL: ByteSearch       = ByteSearch { needle: 0x0000000000000061, count: 1 };
    // a e i o u
    pub const VOWELS: ByteSearch      = ByteSearch { needle: 0x0000006165696f75, count: 5 };
    // < > &
    pub const XML_DELIM_3: ByteSearch = ByteSearch { needle: 0x00000000003c3e26, count: 3 };
    // < > & ' "
    pub const XML_DELIM_5: ByteSearch = ByteSearch { needle: 0x0000003c3e262722, count: 5 };

    #[test]
    fn works_as_find_does_for_single_characters() {
        // Quickcheck currently only generates Strings with A-Z, a-z, 0-9
        fn prop(s: String) -> bool {
            s.find(VOWEL) == s.find('a')
        }
        quickcheck(prop as fn(String) -> bool);
    }

    #[test]
    fn works_as_find_does_for_multiple_characters() {
        // Quickcheck currently only generates Strings with A-Z, a-z, 0-9
        fn prop(s: String) -> bool {
            s.find(VOWELS) == s.find(&['a', 'e', 'i', 'o', 'u'][..])
        }
        quickcheck(prop as fn(String) -> bool);
    }

    #[test]
    fn pattern_does_not_backtrack_after_first() {
        let mut searcher = SPACE.into_searcher("hello w ");
        assert_eq!(SearchStep::Reject(0,5), searcher.next());
        assert_eq!(SearchStep::Match(5,6), searcher.next());
        assert_eq!(SearchStep::Reject(6,7), searcher.next());
        assert_eq!(SearchStep::Match(7,8), searcher.next());
        assert_eq!(SearchStep::Done, searcher.next());
    }

    #[test]
    fn space_is_found() {
        // Since the algorithm operates on 16-byte chunks, it's
        // important to cover tests around that boundary. Since 16
        // isn't that big of a number, we might as well do all of
        // them.

        assert_eq!(Some(0),  SPACE.find(" "));
        assert_eq!(Some(1),  SPACE.find("0 "));
        assert_eq!(Some(2),  SPACE.find("01 "));
        assert_eq!(Some(3),  SPACE.find("012 "));
        assert_eq!(Some(4),  SPACE.find("0123 "));
        assert_eq!(Some(5),  SPACE.find("01234 "));
        assert_eq!(Some(6),  SPACE.find("012345 "));
        assert_eq!(Some(7),  SPACE.find("0123456 "));
        assert_eq!(Some(8),  SPACE.find("01234567 "));
        assert_eq!(Some(9),  SPACE.find("012345678 "));
        assert_eq!(Some(10), SPACE.find("0123456789 "));
        assert_eq!(Some(11), SPACE.find("0123456789A "));
        assert_eq!(Some(12), SPACE.find("0123456789AB "));
        assert_eq!(Some(13), SPACE.find("0123456789ABC "));
        assert_eq!(Some(14), SPACE.find("0123456789ABCD "));
        assert_eq!(Some(15), SPACE.find("0123456789ABCDE "));
        assert_eq!(Some(16), SPACE.find("0123456789ABCDEF "));
        assert_eq!(Some(17), SPACE.find("0123456789ABCDEFG "));
    }

    #[test]
    fn space_not_found() {
        // Since the algorithm operates on 16-byte chunks, it's
        // important to cover tests around that boundary. Since 16
        // isn't that big of a number, we might as well do all of
        // them.

        assert_eq!(None, SPACE.find(""));
        assert_eq!(None, SPACE.find("0"));
        assert_eq!(None, SPACE.find("01"));
        assert_eq!(None, SPACE.find("012"));
        assert_eq!(None, SPACE.find("0123"));
        assert_eq!(None, SPACE.find("01234"));
        assert_eq!(None, SPACE.find("012345"));
        assert_eq!(None, SPACE.find("0123456"));
        assert_eq!(None, SPACE.find("01234567"));
        assert_eq!(None, SPACE.find("012345678"));
        assert_eq!(None, SPACE.find("0123456789"));
        assert_eq!(None, SPACE.find("0123456789A"));
        assert_eq!(None, SPACE.find("0123456789AB"));
        assert_eq!(None, SPACE.find("0123456789ABC"));
        assert_eq!(None, SPACE.find("0123456789ABCD"));
        assert_eq!(None, SPACE.find("0123456789ABCDE"));
        assert_eq!(None, SPACE.find("0123456789ABCDEF"));
        assert_eq!(None, SPACE.find("0123456789ABCDEFG"));
    }

    #[test]
    fn xml_delim_3_is_found() {
        assert_eq!(Some(0), XML_DELIM_3.find("<"));
        assert_eq!(Some(0), XML_DELIM_3.find(">"));
        assert_eq!(Some(0), XML_DELIM_3.find("&"));
        assert_eq!(None,    XML_DELIM_3.find(""));
    }

    #[test]
    fn xml_delim_5_is_found() {
        assert_eq!(Some(0), XML_DELIM_5.find("<"));
        assert_eq!(Some(0), XML_DELIM_5.find(">"));
        assert_eq!(Some(0), XML_DELIM_5.find("&"));
        assert_eq!(Some(0), XML_DELIM_5.find("'"));
        assert_eq!(Some(0), XML_DELIM_5.find("\""));
        assert_eq!(None,    XML_DELIM_5.find(""));
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use super::test::{SPACE,XML_DELIM_3,XML_DELIM_5};
    use std::iter;

    fn prefix_string() -> String {
        iter::repeat("a").take(5 * 1024 * 1024).collect()
    }

    fn bench_space<F>(b: &mut test::Bencher, f: F)
        where F: Fn(&str) -> Option<usize>
    {
        let mut haystack = prefix_string();
        haystack.push(' ');

        b.iter(|| test::black_box(f(&haystack)));
        b.bytes = haystack.len() as u64;
    }

    #[bench]
    fn space_assembly(b: &mut test::Bencher) {
        bench_space(b, |hs| SPACE.find(hs))
    }

    #[bench]
    fn space_assembly_as_pattern(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(SPACE))
    }

    #[bench]
    fn space_find_string(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(" "))
    }

    #[bench]
    fn space_find_char(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(' '))
    }

    #[bench]
    fn space_find_char_set(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.find(&[' '][..]))
    }

    #[bench]
    fn space_find_byte(b: &mut test::Bencher) {
        bench_space(b, |hs| hs.as_bytes().iter().position(|&v| v == b' '))
    }

    fn bench_xml_delim_3<F>(b: &mut test::Bencher, f: F)
        where F: Fn(&str) -> Option<usize>
    {
        let mut haystack = prefix_string();
        haystack.push('&');

        b.iter(|| test::black_box(f(&haystack)));
        b.bytes = haystack.len() as u64;
    }

    #[bench]
    fn xml_delim_3_assembly(b: &mut test::Bencher) {
        bench_xml_delim_3(b, |hs| XML_DELIM_3.find(hs))
    }

    #[bench]
    fn xml_delim_3_assembly_as_pattern(b: &mut test::Bencher) {
        bench_xml_delim_3(b, |hs| hs.find(XML_DELIM_3))
    }

    #[bench]
    fn xml_delim_3_find_char_set(b: &mut test::Bencher) {
        bench_xml_delim_3(b, |hs| hs.find(&['<', '>', '&'][..]))
    }

    fn bench_xml_delim_5<F>(b: &mut test::Bencher, f: F)
        where F: Fn(&str) -> Option<usize>
    {
        let mut haystack = prefix_string();
        haystack.push('"');

        b.iter(|| test::black_box(f(&haystack)));
        b.bytes = haystack.len() as u64;
    }

    #[bench]
    fn xml_delim_5_assembly(b: &mut test::Bencher) {
        bench_xml_delim_5(b, |hs| XML_DELIM_5.find(hs))
    }

    #[bench]
    fn xml_delim_5_assembly_as_pattern(b: &mut test::Bencher) {
        bench_xml_delim_5(b, |hs| hs.find(XML_DELIM_5))
    }

    #[bench]
    fn xml_delim_5_find_char_set(b: &mut test::Bencher) {
        bench_xml_delim_5(b, |hs| hs.find(&['<', '>', '&', '\'', '"'][..]))
    }
}
<|endoftext|>


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

use bit_field::BitField;
use std::fmt::{self, Debug, Display, Formatter};
use thiserror::Error;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FieldInfo {
    pub name: &'static str,
    pub start: usize,
    pub width: usize,
    pub value: u64,
    pub decoded: Option<Decoded>,
}

impl FieldInfo {
    fn get(register: u64, name: &'static str, start: usize, end: usize) -> Self {
        let value = register.get_bits(start..end);
        Self {
            name,
            start,
            width: end - start,
            value,
            decoded: None,
        }
    }

    fn get_bit(register: u64, name: &'static str, bit: usize) -> Self {
        Self::get(register, name, bit, bit + 1)
    }
}

impl Display for FieldInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        if self.width == 1 {
            write!(
                f,
                "{}:{}",
                self.name,
                if self.value == 1 { "true" } else { "false" }
            )
        } else {
            write!(f, "{}:{:#x}", self.name, self.value)
        }
    }
}

#[derive(Debug, Error)]
pub enum DecodeError {
    #[error("Invalid ESR, res0 is {res0}")]
    InvalidRes0 { res0: u64 },
    #[error("Invalid EC {ec}")]
    InvalidEc { ec: u64 },
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum SyndromeAccessSize {
    Byte = 0b00,
    Halfword = 0b01,
    Word = 0b10,
    Doubleword = 0b11,
}

impl Display for SyndromeAccessSize {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let s = match self {
            Self::Byte => "byte",
            Self::Halfword => "halfword",
            Self::Word => "word",
            Self::Doubleword => "doubleword",
        };
        write!(f, "{}", s)
    }
}

fn decode_iss_data_abort(iss: u64) -> Decoded {
    let isv = FieldInfo::get_bit(iss, "ISV", 24);
    let sas = FieldInfo::get(iss, "SAS", 22, 24);
    let sas_decoded = match sas.value {
        0b00 => SyndromeAccessSize::Byte,
        0b01 => SyndromeAccessSize::Halfword,
        0b10 => SyndromeAccessSize::Word,
        0b11 => SyndromeAccessSize::Doubleword,
        _ => unreachable!(),
    };
    let sse = FieldInfo::get_bit(iss, "SSE", 21);
    let srt = FieldInfo::get(iss, "SRT", 16, 21);
    let sf = FieldInfo::get_bit(iss, "SF", 15);
    let ar = FieldInfo::get_bit(iss, "AR", 14);
    let vncr = FieldInfo::get_bit(iss, "VNCR", 13);
    let fnv = FieldInfo::get_bit(iss, "FnV", 10);
    let ea = FieldInfo::get_bit(iss, "EA", 9);
    let cm = FieldInfo::get_bit(iss, "CM", 8);
    let s1ptw = FieldInfo::get_bit(iss, "S1PTW", 7);
    let wnr = FieldInfo::get_bit(iss, "WnR", 6);
    let dfsc = FieldInfo::get(iss, "DFSC", 0, 6);
    let description = format!("{}, SAS:{}", isv, sas_decoded);
    Decoded {
        fields: vec![
            isv, sas, sse, srt, sf, ar, vncr, fnv, ea, cm, s1ptw, wnr, dfsc,
        ],
        description,
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Decoded {
    pub fields: Vec<FieldInfo>,
    pub description: String,
}

pub fn decode(esr: u64) -> Result<Decoded, DecodeError> {
    let res0 = FieldInfo::get(esr, "RES0", 37, 64);
    let iss2 = FieldInfo::get(esr, "ISS2", 32, 37);
    let ec = FieldInfo::get(esr, "EC", 26, 32);
    let il = FieldInfo::get_bit(esr, "IL", 25);
    let iss = FieldInfo::get(esr, "ISS", 0, 25);
    if res0.value != 0 {
        return Err(DecodeError::InvalidRes0 { res0: res0.value });
    }
    let (class, iss_decoded) = match ec.value {
        0b000000 => ("Unknown reason", None),
        0b000001 => ("Wrapped WF* instruction execution", None),
        0b000011 => ("Trapped MCR or MRC access with coproc=0b1111", None),
        0b000100 => ("Trapped MCRR or MRRC access with coproc=0b1111", None),
        0b000101 => ("Trapped MCR or MRC access with coproc=0b1110", None),
        0b000110 => ("Trapped LDC or STC access", None),
        0b000111 => ("Trapped access to SVE, Advanced SIMD or floating point", None),
        0b001010 => ("Trapped execution of an LD64B, ST64B, ST64BV, or ST64BV0 instruction", None),
        0b001100 => ("Trapped MRRC access with (coproc==0b1110)", None),
        0b001101 => ("Branch Target Exception", None),
        0b001110 => ("Illegal Execution state", None),
        0b010001 => ("SVC instruction execution in AArch32 state", None),
        0b010101 => ("SVC instruction execution in AArch64 state", None),
        0b011000 => ("Trapped MSR, MRS or System instruction execution in AArch64 state", None),
        0b011001 => ("Access to SVE functionality trapped as a result of CPACR_EL1.ZEN, CPTR_EL2.ZEN, CPTR_EL2.TZ, or CPTR_EL3.EZ", None),
        0b011100 => ("Exception from a Pointer Authentication instruction authentication failure", None),
        0b100000 => ("Instruction Abort from a lower Exception level", None),
        0b100001 => ("Instruction Abort taken without a change in Exception level", None),
        0b100010 => ("PC alignment fault exception", None),
        0b100100 => ("Data Abort from a lower Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100101 => ("Data Abort taken without a change in Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100110 => ("SP alignment fault exception", None),
        0b101000 => ("Trapped floating-point exception taken from AArch32 state", None),
        0b101100 => ("Trapped floating-point exception taken from AArch64 state", None),
        0b101111 => ("SError interrupt", None),
        0b110000 => ("Breakpoint exception from a lower Exception level", None),
        0b110001 => ("Breakpoint exception taken without a change in Exception level", None),
        0b110010 => ("Software Step exception from a lower Exception level", None),
        0b110011 => ("Software Step exception taken without a change in Exception level", None),
        0b110100 => ("Watchpoint exception from a lower Exception level", None),
        0b110101 => ("Watchpoint exception taken without a change in Exception level", None),
        0b111000 => ("BKPT instruction execution in AArch32 state", None),
        0b111100 => ("BRK instruction execution in AArch64 state", None),
        _ => return Err(DecodeError::InvalidEc { ec: ec.value }),
    };
    let description = if let Some(iss_decoded) = &iss_decoded {
        format!(
            "EC:{:#08b} '{}', {}, {} ({}), {}",
            ec.value, class, il, iss, iss_decoded.description, iss2
        )
    } else {
        format!(
            "EC:{:#08b} '{}', {}, {}, {}",
            ec.value, class, il, iss, iss2
        )
    };
    let iss = FieldInfo {
        decoded: iss_decoded,
        ..iss
    };
    Ok(Decoded {
        fields: vec![res0, iss2, ec, il, iss],
        description,
    })
}
<commit_msg>Print fields in both hex and binary, with appropriate width.<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use bit_field::BitField;
use std::fmt::{self, Debug, Display, Formatter};
use thiserror::Error;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FieldInfo {
    pub name: &'static str,
    pub start: usize,
    pub width: usize,
    pub value: u64,
    pub decoded: Option<Decoded>,
}

impl FieldInfo {
    fn get(register: u64, name: &'static str, start: usize, end: usize) -> Self {
        let value = register.get_bits(start..end);
        Self {
            name,
            start,
            width: end - start,
            value,
            decoded: None,
        }
    }

    fn get_bit(register: u64, name: &'static str, bit: usize) -> Self {
        Self::get(register, name, bit, bit + 1)
    }
}

impl Display for FieldInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        if self.width == 1 {
            write!(
                f,
                "{}: {}",
                self.name,
                if self.value == 1 { "true" } else { "false" }
            )
        } else {
            write!(
                f,
                "{}: {:#02$x} {1:#03$b}",
                self.name,
                self.value,
                (self.width + 3) / 4 + 2,
                self.width + 2
            )
        }
    }
}

#[derive(Debug, Error)]
pub enum DecodeError {
    #[error("Invalid ESR, res0 is {res0}")]
    InvalidRes0 { res0: u64 },
    #[error("Invalid EC {ec}")]
    InvalidEc { ec: u64 },
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum SyndromeAccessSize {
    Byte = 0b00,
    Halfword = 0b01,
    Word = 0b10,
    Doubleword = 0b11,
}

impl Display for SyndromeAccessSize {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let s = match self {
            Self::Byte => "byte",
            Self::Halfword => "halfword",
            Self::Word => "word",
            Self::Doubleword => "doubleword",
        };
        write!(f, "{}", s)
    }
}

fn decode_iss_data_abort(iss: u64) -> Decoded {
    let isv = FieldInfo::get_bit(iss, "ISV", 24);
    let sas = FieldInfo::get(iss, "SAS", 22, 24);
    let sas_decoded = match sas.value {
        0b00 => SyndromeAccessSize::Byte,
        0b01 => SyndromeAccessSize::Halfword,
        0b10 => SyndromeAccessSize::Word,
        0b11 => SyndromeAccessSize::Doubleword,
        _ => unreachable!(),
    };
    let sse = FieldInfo::get_bit(iss, "SSE", 21);
    let srt = FieldInfo::get(iss, "SRT", 16, 21);
    let sf = FieldInfo::get_bit(iss, "SF", 15);
    let ar = FieldInfo::get_bit(iss, "AR", 14);
    let vncr = FieldInfo::get_bit(iss, "VNCR", 13);
    let fnv = FieldInfo::get_bit(iss, "FnV", 10);
    let ea = FieldInfo::get_bit(iss, "EA", 9);
    let cm = FieldInfo::get_bit(iss, "CM", 8);
    let s1ptw = FieldInfo::get_bit(iss, "S1PTW", 7);
    let wnr = FieldInfo::get_bit(iss, "WnR", 6);
    let dfsc = FieldInfo::get(iss, "DFSC", 0, 6);
    let description = format!("{}, SAS:{}", isv, sas_decoded);
    Decoded {
        fields: vec![
            isv, sas, sse, srt, sf, ar, vncr, fnv, ea, cm, s1ptw, wnr, dfsc,
        ],
        description,
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Decoded {
    pub fields: Vec<FieldInfo>,
    pub description: String,
}

pub fn decode(esr: u64) -> Result<Decoded, DecodeError> {
    let res0 = FieldInfo::get(esr, "RES0", 37, 64);
    let iss2 = FieldInfo::get(esr, "ISS2", 32, 37);
    let ec = FieldInfo::get(esr, "EC", 26, 32);
    let il = FieldInfo::get_bit(esr, "IL", 25);
    let iss = FieldInfo::get(esr, "ISS", 0, 25);
    if res0.value != 0 {
        return Err(DecodeError::InvalidRes0 { res0: res0.value });
    }
    let (class, iss_decoded) = match ec.value {
        0b000000 => ("Unknown reason", None),
        0b000001 => ("Wrapped WF* instruction execution", None),
        0b000011 => ("Trapped MCR or MRC access with coproc=0b1111", None),
        0b000100 => ("Trapped MCRR or MRRC access with coproc=0b1111", None),
        0b000101 => ("Trapped MCR or MRC access with coproc=0b1110", None),
        0b000110 => ("Trapped LDC or STC access", None),
        0b000111 => ("Trapped access to SVE, Advanced SIMD or floating point", None),
        0b001010 => ("Trapped execution of an LD64B, ST64B, ST64BV, or ST64BV0 instruction", None),
        0b001100 => ("Trapped MRRC access with (coproc==0b1110)", None),
        0b001101 => ("Branch Target Exception", None),
        0b001110 => ("Illegal Execution state", None),
        0b010001 => ("SVC instruction execution in AArch32 state", None),
        0b010101 => ("SVC instruction execution in AArch64 state", None),
        0b011000 => ("Trapped MSR, MRS or System instruction execution in AArch64 state", None),
        0b011001 => ("Access to SVE functionality trapped as a result of CPACR_EL1.ZEN, CPTR_EL2.ZEN, CPTR_EL2.TZ, or CPTR_EL3.EZ", None),
        0b011100 => ("Exception from a Pointer Authentication instruction authentication failure", None),
        0b100000 => ("Instruction Abort from a lower Exception level", None),
        0b100001 => ("Instruction Abort taken without a change in Exception level", None),
        0b100010 => ("PC alignment fault exception", None),
        0b100100 => ("Data Abort from a lower Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100101 => ("Data Abort taken without a change in Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100110 => ("SP alignment fault exception", None),
        0b101000 => ("Trapped floating-point exception taken from AArch32 state", None),
        0b101100 => ("Trapped floating-point exception taken from AArch64 state", None),
        0b101111 => ("SError interrupt", None),
        0b110000 => ("Breakpoint exception from a lower Exception level", None),
        0b110001 => ("Breakpoint exception taken without a change in Exception level", None),
        0b110010 => ("Software Step exception from a lower Exception level", None),
        0b110011 => ("Software Step exception taken without a change in Exception level", None),
        0b110100 => ("Watchpoint exception from a lower Exception level", None),
        0b110101 => ("Watchpoint exception taken without a change in Exception level", None),
        0b111000 => ("BKPT instruction execution in AArch32 state", None),
        0b111100 => ("BRK instruction execution in AArch64 state", None),
        _ => return Err(DecodeError::InvalidEc { ec: ec.value }),
    };
    let description = if let Some(iss_decoded) = &iss_decoded {
        format!(
            "EC:{:#08b} '{}', {}, {} ({}), {}",
            ec.value, class, il, iss, iss_decoded.description, iss2
        )
    } else {
        format!(
            "EC:{:#08b} '{}', {}, {}, {}",
            ec.value, class, il, iss, iss2
        )
    };
    let iss = FieldInfo {
        decoded: iss_decoded,
        ..iss
    };
    Ok(Decoded {
        fields: vec![res0, iss2, ec, il, iss],
        description,
    })
}
<|endoftext|>


<commit_before>#[derive(Clone, Debug)]
pub enum Type {
    Int8,
    Int16,
    Int32,
    Int64,
    ISize,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    USize,
    Float32,
    Float64,
    Char,
    Bool,
    Array(Box<Type>, usize),
    Compound(Vec<Field>, usize)
}

impl Type {
    pub fn size(&self) -> usize {
        match self {
            &Type::Int8 | &Type::UInt8 | &Type::Bool => 1,
            &Type::Int16 | &Type::UInt16 => 2,
            &Type::Int32 | &Type::UInt32 | &Type::Float32 => 4,
            &Type::Int64 | &Type::UInt64 | &Type::Float64 => 8,
            &Type::Char => ::std::mem::size_of::<char>(),
            &Type::USize => ::std::mem::size_of::<usize>(),
            &Type::ISize => ::std::mem::size_of::<isize>(),
            &Type::Array(ref ty, num) => ty.size() * num,
            &Type::Compound(_, size) => size,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Field {
    pub ty: Type,
    pub name: String,
    pub offset: usize,
}

impl Field {
    pub fn new<S, U>(ty: &Type, name: S, offset: U) -> Field
    where S: Into<String>, U: Into<usize> {
        Field {
            ty: ty.clone(),
            name: name.into(),
            offset: offset.into()
        }
    }
}

pub trait TypeInfo : Copy {
    fn type_info() -> Type;
}

macro_rules! impl_atomic {
    ($t:ty, $i:ident) => (
        impl $crate::TypeInfo for $t {
            #[inline(always)]
            fn type_info() -> $crate::Type {
                $crate::Type::$i
            }
        }
    )
}

impl_atomic!(i8, Int8);
impl_atomic!(i16, Int16);
impl_atomic!(i32, Int32);
impl_atomic!(i64, Int64);
impl_atomic!(isize, ISize);
impl_atomic!(u8, UInt8);
impl_atomic!(u16, UInt16);
impl_atomic!(u32, UInt32);
impl_atomic!(u64, UInt64);
impl_atomic!(usize, USize);
impl_atomic!(f32, Float32);
impl_atomic!(f64, Float64);
impl_atomic!(char, Char);
impl_atomic!(bool, Bool);

macro_rules! impl_array {
    ($($n:expr),*$(,)*) => {
        $(
            impl<T: $crate::TypeInfo> $crate::TypeInfo for [T; $n] {
                #[inline(always)]
                fn type_info() -> $crate::Type {
                    $crate::Type::Array(
                        Box::new(<T as $crate::TypeInfo>::type_info()),
                        $n
                    )
                }
            }
        )*
    };
}

impl_array!(
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
);

#[macro_export]
macro_rules! def {
    ($($(#[$attr:meta])* struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $(pub $i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { pub $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    (@impl $s:ident { $($i:ident: $t:ty),+ }) => (
        impl $crate::TypeInfo for $s {
            fn type_info() -> $crate::Type {
                let base = 0usize as *const $s;
                $crate::Type::Compound(vec![$(
                    $crate::Field::new(
                        &<$t as $crate::TypeInfo>::type_info(),
                        stringify!($i),
                        unsafe { &((*base).$i) as *const $t as usize}
                    )
                ),*], ::std::mem::size_of::<$s>())
            }
        }
    );
}

#[cfg(test)]
pub mod tests {
    use super::TypeInfo;

    def! {
        #[derive(Debug)]
        pub struct Foo {
            pub a: bool,
            pub b: i32,
        }

        pub struct Bar {
            pub foo: [Foo; 2],
            pub s: f64
        }
    }

    #[test]
    fn test_smoke() {
        println!("{:#?}", Bar::type_info());
    }
}
<commit_msg>Added Type::{is_array, is_compound, is_scalar}<commit_after>#[derive(Clone, Debug)]
pub enum Type {
    Int8,
    Int16,
    Int32,
    Int64,
    ISize,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    USize,
    Float32,
    Float64,
    Char,
    Bool,
    Array(Box<Type>, usize),
    Compound(Vec<Field>, usize)
}

impl Type {
    pub fn size(&self) -> usize {
        match self {
            &Type::Int8 | &Type::UInt8 | &Type::Bool => 1,
            &Type::Int16 | &Type::UInt16 => 2,
            &Type::Int32 | &Type::UInt32 | &Type::Float32 => 4,
            &Type::Int64 | &Type::UInt64 | &Type::Float64 => 8,
            &Type::Char => ::std::mem::size_of::<char>(),
            &Type::USize => ::std::mem::size_of::<usize>(),
            &Type::ISize => ::std::mem::size_of::<isize>(),
            &Type::Array(ref ty, num) => ty.size() * num,
            &Type::Compound(_, size) => size,
        }
    }

    pub fn is_array(&self) -> bool {
        if let &Type::Array(_, _) = self { true } else { false }
    }

    pub fn is_compound(&self) -> bool {
        if let &Type::Compound(_, _) = self { true } else { false }
    }

    pub fn is_scalar(&self) -> bool {
        !self.is_array() && !self.is_compound()
    }
}

#[derive(Clone, Debug)]
pub struct Field {
    pub ty: Type,
    pub name: String,
    pub offset: usize,
}

impl Field {
    pub fn new<S, U>(ty: &Type, name: S, offset: U) -> Field
    where S: Into<String>, U: Into<usize> {
        Field {
            ty: ty.clone(),
            name: name.into(),
            offset: offset.into()
        }
    }
}

pub trait TypeInfo : Copy {
    fn type_info() -> Type;
}

macro_rules! impl_atomic {
    ($t:ty, $i:ident) => (
        impl $crate::TypeInfo for $t {
            #[inline(always)]
            fn type_info() -> $crate::Type {
                $crate::Type::$i
            }
        }
    )
}

impl_atomic!(i8, Int8);
impl_atomic!(i16, Int16);
impl_atomic!(i32, Int32);
impl_atomic!(i64, Int64);
impl_atomic!(isize, ISize);
impl_atomic!(u8, UInt8);
impl_atomic!(u16, UInt16);
impl_atomic!(u32, UInt32);
impl_atomic!(u64, UInt64);
impl_atomic!(usize, USize);
impl_atomic!(f32, Float32);
impl_atomic!(f64, Float64);
impl_atomic!(char, Char);
impl_atomic!(bool, Bool);

macro_rules! impl_array {
    ($($n:expr),*$(,)*) => {
        $(
            impl<T: $crate::TypeInfo> $crate::TypeInfo for [T; $n] {
                #[inline(always)]
                fn type_info() -> $crate::Type {
                    $crate::Type::Array(
                        Box::new(<T as $crate::TypeInfo>::type_info()),
                        $n
                    )
                }
            }
        )*
    };
}

impl_array!(
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
);

#[macro_export]
macro_rules! def {
    ($($(#[$attr:meta])* struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $(pub $i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { pub $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    (@impl $s:ident { $($i:ident: $t:ty),+ }) => (
        impl $crate::TypeInfo for $s {
            fn type_info() -> $crate::Type {
                let base = 0usize as *const $s;
                $crate::Type::Compound(vec![$(
                    $crate::Field::new(
                        &<$t as $crate::TypeInfo>::type_info(),
                        stringify!($i),
                        unsafe { &((*base).$i) as *const $t as usize}
                    )
                ),*], ::std::mem::size_of::<$s>())
            }
        }
    );
}

#[cfg(test)]
pub mod tests {
    use super::TypeInfo;

    def! {
        #[derive(Debug)]
        pub struct Foo {
            pub a: bool,
            pub b: i32,
        }

        pub struct Bar {
            pub foo: [Foo; 2],
            pub s: f64
        }
    }

    #[test]
    fn test_smoke() {
        println!("{:#?}", Bar::type_info());
    }
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]
extern crate conduit;
extern crate route_recognizer as router;

use std::collections::hash_map::{Entry, HashMap};
use std::error::Error;
use std::fmt;

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use router::{Match, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<Box<dyn Handler>>>,
}

#[derive(Debug)]
pub struct RouterError(String);

impl RouteBuilder {
    pub fn new() -> RouteBuilder {
        RouteBuilder {
            routers: HashMap::new(),
        }
    }

    #[allow(clippy::borrowed_box)]
    pub fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&'a Box<dyn Handler>>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path),
            None => Err(format!("No router found for {:?}", method)),
        }
        .map_err(RouterError)
    }

    pub fn map<'a, H: Handler>(
        &'a mut self,
        method: Method,
        pattern: &str,
        handler: H,
    ) -> &'a mut RouteBuilder {
        {
            let router = match self.routers.entry(method) {
                Entry::Occupied(e) => e.into_mut(),
                Entry::Vacant(e) => e.insert(Router::new()),
            };
            router.add(pattern, Box::new(handler));
        }
        self
    }

    pub fn get<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => return Err(box_error(e)),
            }
        };

        {
            let extensions = request.mut_extensions();
            extensions.insert(m.params.clone());
        }

        (*m.handler).call(request)
    }
}

impl Error for RouterError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for RouterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a router::Params;
}

pub fn params(req: &dyn RequestExt) -> &router::Params {
    req.extensions()
        .find::<router::Params>()
        .expect("Missing params")
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a router::Params {
        params(self)
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test;

    use std::io;
    use std::net::SocketAddr;

    use {RequestParams, RouteBuilder};

    use self::conduit_test::ResponseExt;
    use conduit::{
        Body, Extensions, Handler, HeaderMap, Host, Method, Response, Scheme, StatusCode, TypeMap,
        Version,
    };

    struct RequestSentinel {
        method: Method,
        path: String,
        extensions: conduit::Extensions,
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_string(),
                extensions: TypeMap::new(),
                method,
            }
        }
    }

    impl conduit::RequestExt for RequestSentinel {
        fn http_version(&self) -> Version {
            unimplemented!()
        }
        fn method(&self) -> &Method {
            &self.method
        }
        fn scheme(&self) -> Scheme {
            unimplemented!()
        }
        fn host(&self) -> Host<'_> {
            unimplemented!()
        }
        fn virtual_root(&self) -> Option<&str> {
            unimplemented!()
        }
        fn path(&self) -> &str {
            &self.path
        }
        fn path_mut(&mut self) -> &mut String {
            &mut self.path
        }
        fn query_string(&self) -> Option<&str> {
            unimplemented!()
        }
        fn remote_addr(&self) -> SocketAddr {
            unimplemented!()
        }
        fn content_length(&self) -> Option<u64> {
            unimplemented!()
        }
        fn headers(&self) -> &HeaderMap {
            unimplemented!()
        }
        fn body(&mut self) -> &mut dyn io::Read {
            unimplemented!()
        }
        fn extensions(&self) -> &Extensions {
            &self.extensions
        }
        fn mut_extensions(&mut self) -> &mut Extensions {
            &mut self.extensions
        }
    }

    #[test]
    fn basic_get() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET"[..]);
    }

    #[test]
    fn basic_post() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST"[..]);
    }

    #[test]
    fn nonexistent_route() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/nonexistent");
        router.call(&mut req).err().expect("No response");
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let mut res = vec![];
        res.push(req.params()["id"].clone());
        res.push(format!("{:?}", req.method()));

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<commit_msg>Add `RoutePattern` to request extensions<commit_after>#![warn(rust_2018_idioms)]
extern crate conduit;
extern crate route_recognizer as router;

use std::collections::hash_map::{Entry, HashMap};
use std::error::Error;
use std::fmt;

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use router::{Match, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone)]
struct RoutePattern(String);

pub struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

#[derive(Debug)]
pub struct RouterError(String);

impl RouteBuilder {
    pub fn new() -> RouteBuilder {
        RouteBuilder {
            routers: HashMap::new(),
        }
    }

    #[allow(clippy::borrowed_box)]
    pub fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path),
            None => Err(format!("No router found for {:?}", method)),
        }
        .map_err(RouterError)
    }

    pub fn map<'a, H: Handler>(
        &'a mut self,
        method: Method,
        pattern: &str,
        handler: H,
    ) -> &'a mut RouteBuilder {
        {
            let router = match self.routers.entry(method) {
                Entry::Occupied(e) => e.into_mut(),
                Entry::Vacant(e) => e.insert(Router::new()),
            };
            let wrapped_handler = WrappedHandler {
                pattern: RoutePattern(pattern.to_string()),
                handler: Box::new(handler)
            };
            router.add(pattern, wrapped_handler);
        }
        self
    }

    pub fn get<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<'a, H: Handler>(&'a mut self, pattern: &str, handler: H) -> &'a mut RouteBuilder {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => return Err(box_error(e)),
            }
        };

        {
            let extensions = request.mut_extensions();
            extensions.insert(m.handler.pattern.clone());
            extensions.insert(m.params.clone());
        }

        (*m.handler.handler).call(request)
    }
}

impl Error for RouterError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for RouterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a router::Params;
}

pub fn params(req: &dyn RequestExt) -> &router::Params {
    req.extensions()
        .find::<router::Params>()
        .expect("Missing params")
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a router::Params {
        params(self)
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test;

    use std::io;
    use std::net::SocketAddr;

    use {RequestParams, RouteBuilder, RoutePattern};

    use self::conduit_test::ResponseExt;
    use conduit::{
        Body, Extensions, Handler, HeaderMap, Host, Method, Response, Scheme, StatusCode, TypeMap,
        Version,
    };

    struct RequestSentinel {
        method: Method,
        path: String,
        extensions: conduit::Extensions,
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_string(),
                extensions: TypeMap::new(),
                method,
            }
        }
    }

    impl conduit::RequestExt for RequestSentinel {
        fn http_version(&self) -> Version {
            unimplemented!()
        }
        fn method(&self) -> &Method {
            &self.method
        }
        fn scheme(&self) -> Scheme {
            unimplemented!()
        }
        fn host(&self) -> Host<'_> {
            unimplemented!()
        }
        fn virtual_root(&self) -> Option<&str> {
            unimplemented!()
        }
        fn path(&self) -> &str {
            &self.path
        }
        fn path_mut(&mut self) -> &mut String {
            &mut self.path
        }
        fn query_string(&self) -> Option<&str> {
            unimplemented!()
        }
        fn remote_addr(&self) -> SocketAddr {
            unimplemented!()
        }
        fn content_length(&self) -> Option<u64> {
            unimplemented!()
        }
        fn headers(&self) -> &HeaderMap {
            unimplemented!()
        }
        fn body(&mut self) -> &mut dyn io::Read {
            unimplemented!()
        }
        fn extensions(&self) -> &Extensions {
            &self.extensions
        }
        fn mut_extensions(&mut self) -> &mut Extensions {
            &mut self.extensions
        }
    }

    #[test]
    fn basic_get() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn nonexistent_route() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/nonexistent");
        router.call(&mut req).err().expect("No response");
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let mut res = vec![];
        res.push(req.params()["id"].clone());
        res.push(format!("{:?}", req.method()));
        res.push(req.extensions().find::<RoutePattern>().unwrap().0.clone());

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<|endoftext|>


<commit_before>//! Geometric types for 2D lattice-shaped puzzles.

use std::ops::{Add, Mul, Sub, Neg, Index, IndexMut, Range};

/// A two-dimensional lattice point.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Point(pub i32, pub i32);

/// A size of a rectangle.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Size(pub i32, pub i32);

/// A difference between two `Point`s.
///
/// `Point(y0, x0)` - `Point(y1, x1) == `Move(y0 - y1, x0 - x1)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Move(pub i32, pub i32);

/// A 2x2 rotation matrix.
///
/// `Rotation(yy, yx, xy, xx) * Move(y, x) == Move(yy*y + yx*x, xy*y + xx*x)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Rotation(i32, i32, i32, i32);

/// An upward `Move` vector.
pub const MOVE_UP: Move = Move(-1, 0);

/// A rightward `Move` vector.
pub const MOVE_RIGHT: Move = Move(0, 1);

/// A downward `Move` vector.
pub const MOVE_DOWN: Move = Move(1, 0);

/// A leftward `Move` vector.
pub const MOVE_LEFT: Move = Move(0, -1);

/// `Move` vectors that is toward four adjacent points.
pub const MOVE_ALL_DIRECTIONS: [Move; 4] = [MOVE_UP, MOVE_RIGHT, MOVE_DOWN, MOVE_LEFT];

/// `Move` vectors that is toward eight adjacent points.
pub const MOVE_ALL_ADJACENTS: [Move; 8] =
    [MOVE_UP, Move(-1, 1), MOVE_RIGHT, Move(1, 1), MOVE_DOWN, Move(1, -1), MOVE_LEFT, Move(-1, -1)];

impl Add<Move> for Point {
    type Output = Point;

    #[inline]
    fn add(self, other: Move) -> Point {
        Point(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Point> for Point {
    type Output = Move;

    #[inline]
    fn sub(self, other: Point) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Add<Move> for Move {
    type Output = Move;

    #[inline]
    fn add(self, other: Move) -> Move {
        Move(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Move> for Move {
    type Output = Move;

    #[inline]
    fn sub(self, other: Move) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Neg for Move {
    type Output = Move;

    #[inline]
    fn neg(self) -> Move {
        Move(-self.0, -self.1)
    }
}

impl Mul<i32> for Move {
    type Output = Move;

    #[inline]
    fn mul(self, other: i32) -> Move {
        Move(self.0 * other, self.1 * other)
    }
}

/// A 0-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW0: Rotation = Rotation(1, 0, 0, 1);

/// A 90-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW90: Rotation = Rotation(0, -1, 1, 0);

/// A 180-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW180: Rotation = Rotation(-1, 0, 0, -1);

/// A 270-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW270: Rotation = Rotation(0, 1, -1, 0);

/// Flip horizontal.
pub const ROT_H_FLIP: Rotation = Rotation(1, 0, 0, -1);

/// Flip vertical.
pub const ROT_V_FLIP: Rotation = Rotation(-1, 0, 0, 1);

impl Mul<Rotation> for Rotation {
    type Output = Rotation;

    #[inline]
    fn mul(self, other: Rotation) -> Rotation {
        Rotation(self.0 * other.0 + self.1 * other.2,
                 self.0 * other.1 + self.1 * other.3,
                 self.2 * other.0 + self.3 * other.2,
                 self.2 * other.1 + self.3 * other.3)
    }
}

impl Mul<Move> for Rotation {
    type Output = Move;

    #[inline]
    fn mul(self, other: Move) -> Move {
        Move(self.0 * other.0 + self.1 * other.1,
             self.2 * other.0 + self.3 * other.1)
    }
}

/// An ID identifying a cell in lattice rectangle.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CellId(usize);

/// An ID being given to cells on outside the rectangle.
pub const CELL_ID_OUTSIDE: CellId = CellId(0);

impl CellId {
    /// Creates a new `CellId` from an ID.
    #[inline]
    pub fn new(id: usize) -> CellId {
        CellId(id)
    }

    /// Gets an ID of the cell.
    #[inline]
    pub fn id(self) -> usize {
        self.0
    }

    /// Returns if the cell is on outside of the rectangle.
    #[inline]
    pub fn is_outside(self) -> bool {
        self == CELL_ID_OUTSIDE
    }
}

/// A representative `Point` of points on outside the rectangle.
const OUTSIDE_POINT: Point = Point(-1, -1);

/// A rectangle area.
pub trait Geom {
    /// Returns the rectangle's size.
    #[inline]
    fn size(&self) -> Size;

    /// Returns the number of the rectangle's rows.
    #[inline]
    fn row(&self) -> i32 {
        self.size().0
    }

    /// Returns the number of the rectangle's columns.
    #[inline]
    fn column(&self) -> i32 {
        self.size().1
    }

    /// Returns the cell length which is contained in the rectangle.
    #[inline]
    fn cell_len(&self) -> usize {
        (self.row() * self.column() + 1) as usize
    }

    /// Returns true if the point is contained in the rectangle.
    #[inline]
    fn contains(&self, p: Point) -> bool {
        let size = self.size();
        0 <= p.0 && p.0 < size.0 && 0 <= p.1 && p.1 < size.1
    }

    /// Convert a point to a corresponding cell ID.
    #[inline]
    fn point_to_cellid(&self, p: Point) -> CellId {
        if self.contains(p) {
            CellId::new((p.0 * self.column() + p.1 + 1) as usize)
        } else {
            CELL_ID_OUTSIDE
        }
    }

    /// Convert a cell ID to a corresponding point.
    #[inline]
    fn cellid_to_point(&self, id: CellId) -> Point {
        if id.is_outside() {
            OUTSIDE_POINT
        } else {
            let idx = id.id() - 1;
            Point((idx as i32) / self.column(), (idx as i32) % self.column())
        }
    }

    /// Returns an iterator iterating all points.
    #[inline]
    fn points(&self) -> Points {
        if self.row() > 0 && self.column() > 0 {
            Points {
                point: Some(Point(0, 0)),
                size: self.size(),
            }
        } else {
            Points {
                point: None,
                size: self.size(),
            }
        }
    }

    /// Returns an iterator iterating all points in the row.
    #[inline]
    fn points_in_row(&self, row: i32) -> PointsInRow {
        PointsInRow {
            row: row,
            columns: 0..self.column(),
        }
    }

    /// Returns an iterator iterating all points in the column.
    #[inline]
    fn points_in_column(&self, column: i32) -> PointsInColumn {
        PointsInColumn {
            column: column,
            rows: 0..self.row(),
        }
    }
}

/// An iterator iterating all points in the rectangle.
#[derive(Copy, Clone, Debug)]
pub struct Points {
    point: Option<Point>,
    size: Size,
}

impl Iterator for Points {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(cur) = self.point {
            let mut next = cur;
            let mut end = false;
            next.1 += 1;
            if next.1 >= self.size.1 {
                next.0 += 1;
                next.1 = 0;
                if next.0 >= self.size.0 {
                    end = true;
                }
            }
            if !end {
                self.point = Some(next);
            } else {
                self.point = None;
            }
            return Some(cur);
        }
        None
    }
}

/// An iterator iterating all points in a row of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInRow {
    row: i32,
    columns: Range<i32>,
}

impl Iterator for PointsInRow {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(c) = self.columns.next() {
            Some(Point(self.row, c))
        } else {
            None
        }
    }
}

/// An iterator iterating all points in a column of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInColumn {
    rows: Range<i32>,
    column: i32,
}

impl Iterator for PointsInColumn {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(r) = self.rows.next() {
            Some(Point(r, self.column))
        } else {
            None
        }
    }
}

/// A table that stores values for each cells.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Table<T> {
    size: Size,
    data: Vec<T>,
}

impl<T> Table<T> {
    /// Creates a new table with data.
    #[inline]
    pub fn new(size: Size, outside: T, mut data: Vec<T>) -> Table<T> {
        assert_eq!((size.0 * size.1) as usize, data.len());
        data.insert(0, outside);
        Table {
            size: size,
            data: data,
        }
    }

    /// Creates a new empty table.
    #[inline]
    pub fn new_empty(size: Size, outside: T, init: T) -> Table<T>
        where T: Clone
    {
        let data = vec![init; (size.0 * size.1) as usize];
        Table::new(size, outside, data)
    }
}

impl<T> Geom for Table<T> {
    #[inline]
    fn size(&self) -> Size {
        self.size
    }
}

impl<T> Index<Point> for Table<T> {
    type Output = T;

    #[inline]
    fn index(&self, p: Point) -> &T {
        let idx = self.point_to_cellid(p).id();
        &self.data[idx]
    }
}

impl<T> IndexMut<Point> for Table<T> {
    #[inline]
    fn index_mut(&mut self, p: Point) -> &mut T {
        let idx = self.point_to_cellid(p).id();
        &mut self.data[idx]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    struct Rect(Size);
    impl Geom for Rect {
        fn size(&self) -> Size {
            self.0
        }
    }

    #[test]
    fn points() {
        let pts = [Point(0, 0),
                   Point(0, 1),
                   Point(0, 2),
                   Point(1, 0),
                   Point(1, 1),
                   Point(1, 2),
                   Point(2, 0),
                   Point(2, 1),
                   Point(2, 2),
                   Point(3, 0),
                   Point(3, 1),
                   Point(3, 2)];
        let rect = Rect(Size(4, 3));
        assert_eq!(&pts[..], &rect.points().collect::<Vec<_>>()[..]);
    }

    #[test]
    fn rotate_mat() {
        let mat = [ROT_CCW0, ROT_CCW90, ROT_CCW180, ROT_CCW270];
        for i in 0..mat.len() {
            for j in 0..mat.len() {
                assert_eq!(mat[(i + j) % mat.len()], mat[i] * mat[j]);
            }
        }
    }

    #[test]
    fn rotate_point() {
        let mat = [ROT_CCW0, ROT_CCW90, ROT_CCW180, ROT_CCW270];
        let vec = [[MOVE_UP, MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT],
                   [MOVE_UP + MOVE_RIGHT,
                    MOVE_LEFT + MOVE_UP,
                    MOVE_DOWN + MOVE_LEFT,
                    MOVE_RIGHT + MOVE_DOWN]];
        for i in 0..mat.len() {
            for v in &vec {
                for j in 0..v.len() {
                    assert_eq!(v[(i + j) % v.len()], mat[i] * v[j]);
                }
            }
        }
    }
}
<commit_msg>Run rustfmt<commit_after>//! Geometric types for 2D lattice-shaped puzzles.

use std::ops::{Add, Index, IndexMut, Mul, Neg, Range, Sub};

/// A two-dimensional lattice point.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Point(pub i32, pub i32);

/// A size of a rectangle.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Size(pub i32, pub i32);

/// A difference between two `Point`s.
///
/// `Point(y0, x0)` - `Point(y1, x1) == `Move(y0 - y1, x0 - x1)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Move(pub i32, pub i32);

/// A 2x2 rotation matrix.
///
/// `Rotation(yy, yx, xy, xx) * Move(y, x) == Move(yy*y + yx*x, xy*y + xx*x)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Rotation(i32, i32, i32, i32);

/// An upward `Move` vector.
pub const MOVE_UP: Move = Move(-1, 0);

/// A rightward `Move` vector.
pub const MOVE_RIGHT: Move = Move(0, 1);

/// A downward `Move` vector.
pub const MOVE_DOWN: Move = Move(1, 0);

/// A leftward `Move` vector.
pub const MOVE_LEFT: Move = Move(0, -1);

/// `Move` vectors that is toward four adjacent points.
pub const MOVE_ALL_DIRECTIONS: [Move; 4] = [MOVE_UP, MOVE_RIGHT, MOVE_DOWN, MOVE_LEFT];

/// `Move` vectors that is toward eight adjacent points.
pub const MOVE_ALL_ADJACENTS: [Move; 8] = [
    MOVE_UP,
    Move(-1, 1),
    MOVE_RIGHT,
    Move(1, 1),
    MOVE_DOWN,
    Move(1, -1),
    MOVE_LEFT,
    Move(-1, -1),
];

impl Add<Move> for Point {
    type Output = Point;

    #[inline]
    fn add(self, other: Move) -> Point {
        Point(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Point> for Point {
    type Output = Move;

    #[inline]
    fn sub(self, other: Point) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Add<Move> for Move {
    type Output = Move;

    #[inline]
    fn add(self, other: Move) -> Move {
        Move(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Move> for Move {
    type Output = Move;

    #[inline]
    fn sub(self, other: Move) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Neg for Move {
    type Output = Move;

    #[inline]
    fn neg(self) -> Move {
        Move(-self.0, -self.1)
    }
}

impl Mul<i32> for Move {
    type Output = Move;

    #[inline]
    fn mul(self, other: i32) -> Move {
        Move(self.0 * other, self.1 * other)
    }
}

/// A 0-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW0: Rotation = Rotation(1, 0, 0, 1);

/// A 90-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW90: Rotation = Rotation(0, -1, 1, 0);

/// A 180-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW180: Rotation = Rotation(-1, 0, 0, -1);

/// A 270-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW270: Rotation = Rotation(0, 1, -1, 0);

/// Flip horizontal.
pub const ROT_H_FLIP: Rotation = Rotation(1, 0, 0, -1);

/// Flip vertical.
pub const ROT_V_FLIP: Rotation = Rotation(-1, 0, 0, 1);

impl Mul<Rotation> for Rotation {
    type Output = Rotation;

    #[inline]
    fn mul(self, other: Rotation) -> Rotation {
        Rotation(
            self.0 * other.0 + self.1 * other.2,
            self.0 * other.1 + self.1 * other.3,
            self.2 * other.0 + self.3 * other.2,
            self.2 * other.1 + self.3 * other.3,
        )
    }
}

impl Mul<Move> for Rotation {
    type Output = Move;

    #[inline]
    fn mul(self, other: Move) -> Move {
        Move(
            self.0 * other.0 + self.1 * other.1,
            self.2 * other.0 + self.3 * other.1,
        )
    }
}

/// An ID identifying a cell in lattice rectangle.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CellId(usize);

/// An ID being given to cells on outside the rectangle.
pub const CELL_ID_OUTSIDE: CellId = CellId(0);

impl CellId {
    /// Creates a new `CellId` from an ID.
    #[inline]
    pub fn new(id: usize) -> CellId {
        CellId(id)
    }

    /// Gets an ID of the cell.
    #[inline]
    pub fn id(self) -> usize {
        self.0
    }

    /// Returns if the cell is on outside of the rectangle.
    #[inline]
    pub fn is_outside(self) -> bool {
        self == CELL_ID_OUTSIDE
    }
}

/// A representative `Point` of points on outside the rectangle.
const OUTSIDE_POINT: Point = Point(-1, -1);

/// A rectangle area.
pub trait Geom {
    /// Returns the rectangle's size.
    #[inline]
    fn size(&self) -> Size;

    /// Returns the number of the rectangle's rows.
    #[inline]
    fn row(&self) -> i32 {
        self.size().0
    }

    /// Returns the number of the rectangle's columns.
    #[inline]
    fn column(&self) -> i32 {
        self.size().1
    }

    /// Returns the cell length which is contained in the rectangle.
    #[inline]
    fn cell_len(&self) -> usize {
        (self.row() * self.column() + 1) as usize
    }

    /// Returns true if the point is contained in the rectangle.
    #[inline]
    fn contains(&self, p: Point) -> bool {
        let size = self.size();
        0 <= p.0 && p.0 < size.0 && 0 <= p.1 && p.1 < size.1
    }

    /// Convert a point to a corresponding cell ID.
    #[inline]
    fn point_to_cellid(&self, p: Point) -> CellId {
        if self.contains(p) {
            CellId::new((p.0 * self.column() + p.1 + 1) as usize)
        } else {
            CELL_ID_OUTSIDE
        }
    }

    /// Convert a cell ID to a corresponding point.
    #[inline]
    fn cellid_to_point(&self, id: CellId) -> Point {
        if id.is_outside() {
            OUTSIDE_POINT
        } else {
            let idx = id.id() - 1;
            Point((idx as i32) / self.column(), (idx as i32) % self.column())
        }
    }

    /// Returns an iterator iterating all points.
    #[inline]
    fn points(&self) -> Points {
        if self.row() > 0 && self.column() > 0 {
            Points {
                point: Some(Point(0, 0)),
                size: self.size(),
            }
        } else {
            Points {
                point: None,
                size: self.size(),
            }
        }
    }

    /// Returns an iterator iterating all points in the row.
    #[inline]
    fn points_in_row(&self, row: i32) -> PointsInRow {
        PointsInRow {
            row: row,
            columns: 0..self.column(),
        }
    }

    /// Returns an iterator iterating all points in the column.
    #[inline]
    fn points_in_column(&self, column: i32) -> PointsInColumn {
        PointsInColumn {
            column: column,
            rows: 0..self.row(),
        }
    }
}

/// An iterator iterating all points in the rectangle.
#[derive(Copy, Clone, Debug)]
pub struct Points {
    point: Option<Point>,
    size: Size,
}

impl Iterator for Points {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(cur) = self.point {
            let mut next = cur;
            let mut end = false;
            next.1 += 1;
            if next.1 >= self.size.1 {
                next.0 += 1;
                next.1 = 0;
                if next.0 >= self.size.0 {
                    end = true;
                }
            }
            if !end {
                self.point = Some(next);
            } else {
                self.point = None;
            }
            return Some(cur);
        }
        None
    }
}

/// An iterator iterating all points in a row of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInRow {
    row: i32,
    columns: Range<i32>,
}

impl Iterator for PointsInRow {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(c) = self.columns.next() {
            Some(Point(self.row, c))
        } else {
            None
        }
    }
}

/// An iterator iterating all points in a column of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInColumn {
    rows: Range<i32>,
    column: i32,
}

impl Iterator for PointsInColumn {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(r) = self.rows.next() {
            Some(Point(r, self.column))
        } else {
            None
        }
    }
}

/// A table that stores values for each cells.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Table<T> {
    size: Size,
    data: Vec<T>,
}

impl<T> Table<T> {
    /// Creates a new table with data.
    #[inline]
    pub fn new(size: Size, outside: T, mut data: Vec<T>) -> Table<T> {
        assert_eq!((size.0 * size.1) as usize, data.len());
        data.insert(0, outside);
        Table {
            size: size,
            data: data,
        }
    }

    /// Creates a new empty table.
    #[inline]
    pub fn new_empty(size: Size, outside: T, init: T) -> Table<T>
    where
        T: Clone,
    {
        let data = vec![init; (size.0 * size.1) as usize];
        Table::new(size, outside, data)
    }
}

impl<T> Geom for Table<T> {
    #[inline]
    fn size(&self) -> Size {
        self.size
    }
}

impl<T> Index<Point> for Table<T> {
    type Output = T;

    #[inline]
    fn index(&self, p: Point) -> &T {
        let idx = self.point_to_cellid(p).id();
        &self.data[idx]
    }
}

impl<T> IndexMut<Point> for Table<T> {
    #[inline]
    fn index_mut(&mut self, p: Point) -> &mut T {
        let idx = self.point_to_cellid(p).id();
        &mut self.data[idx]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    struct Rect(Size);
    impl Geom for Rect {
        fn size(&self) -> Size {
            self.0
        }
    }

    #[test]
    fn points() {
        let pts = [
            Point(0, 0),
            Point(0, 1),
            Point(0, 2),
            Point(1, 0),
            Point(1, 1),
            Point(1, 2),
            Point(2, 0),
            Point(2, 1),
            Point(2, 2),
            Point(3, 0),
            Point(3, 1),
            Point(3, 2),
        ];
        let rect = Rect(Size(4, 3));
        assert_eq!(&pts[..], &rect.points().collect::<Vec<_>>()[..]);
    }

    #[test]
    fn rotate_mat() {
        let mat = [ROT_CCW0, ROT_CCW90, ROT_CCW180, ROT_CCW270];
        for i in 0..mat.len() {
            for j in 0..mat.len() {
                assert_eq!(mat[(i + j) % mat.len()], mat[i] * mat[j]);
            }
        }
    }

    #[test]
    fn rotate_point() {
        let mat = [ROT_CCW0, ROT_CCW90, ROT_CCW180, ROT_CCW270];
        let vec = [
            [MOVE_UP, MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT],
            [
                MOVE_UP + MOVE_RIGHT,
                MOVE_LEFT + MOVE_UP,
                MOVE_DOWN + MOVE_LEFT,
                MOVE_RIGHT + MOVE_DOWN,
            ],
        ];
        for i in 0..mat.len() {
            for v in &vec {
                for j in 0..v.len() {
                    assert_eq!(v[(i + j) % v.len()], mat[i] * v[j]);
                }
            }
        }
    }
}
<|endoftext|>