#![feature(test)]

extern crate byte;
extern crate byteorder;
extern crate test;

use byte::ctx::*;
use byte::*;
use byteorder::*;
use test::black_box;

#[bench]
fn bench_byteorder(b: &mut test::Bencher) {
    b.iter(|| black_box(LittleEndian::read_u16(&black_box([1, 2]))));
    b.bytes = 2;
}

#[bench]
fn bench_read_num(b: &mut test::Bencher) {
    b.iter(|| black_box(black_box([1, 2]).read_with::<u16>(&mut 0, LE).unwrap()));
    b.bytes = 2;
}

#[bench]
fn bench_str(b: &mut test::Bencher) {
    let bytes = b"abcdefghijkl";
    b.iter(|| {
        black_box(
            black_box(bytes)
                .read_with::<&str>(&mut 0, Str::Len(5))
                .unwrap(),
        )
    });
    b.bytes = 5;
}

#[bench]
fn bench_str_hardcode(b: &mut test::Bencher) {
    let bytes = b"abcdefghijkl";
    b.iter(|| black_box(std::str::from_utf8(&black_box(bytes)[0..5]).unwrap()));
    b.bytes = 5;
}

#[bench]
fn bench_example_read(b: &mut test::Bencher) {
    let bytes = black_box([0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0]);
    b.iter(|| black_box(bytes.read_with::<Header>(&mut 0, BE).unwrap()));
    b.bytes = 8;
}

#[bench]
fn bench_example_write(b: &mut test::Bencher) {
    let mut bytes = [0u8; 8];
    b.iter(|| {
        let header = black_box(Header {
            name: "HELLO",
            enabled: false,
        });
        bytes.write_with::<Header>(&mut 0, header, BE).unwrap()
    });
    b.bytes = 8;
}

#[bench]
fn bench_example_read_hardcode(b: &mut test::Bencher) {
    let bytes = black_box([0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0]);
    b.iter(|| black_box(example_read_hardcode(&bytes[..]).unwrap()));
    b.bytes = 8;
}

#[bench]
fn bench_example_write_hardcode(b: &mut test::Bencher) {
    let mut bytes = [0u8; 8];
    b.iter(|| {
        let header = black_box(Header {
            name: "HELLO",
            enabled: false,
        });
        example_write_hardcode(&mut bytes[..], header).unwrap()
    });
    b.bytes = 8;
}

fn example_read_hardcode<'a>(bytes: &'a [u8]) -> Option<Header<'a>> {
    if bytes.len() < 3 {
        return None;
    }

    let name_len = unsafe { *(&bytes[0] as *const _ as *const u16) };
    let name_len = name_len.to_be() as usize;

    if bytes.len() < name_len + 3 {
        return None;
    }

    let name = std::str::from_utf8(&bytes[2..name_len + 2]).unwrap();
    let enabled = bytes[name_len + 2] != 0;

    Some(Header { name, enabled })
}

fn example_write_hardcode(bytes: &mut [u8], header: Header) -> Option<()> {
    if bytes.len() < header.name.len() + 3 {
        return None;
    }

    unsafe { *(&mut bytes[0] as *mut _ as *mut u16) = header.name.len().to_be() as u16 };

    bytes[2..header.name.len() + 2].clone_from_slice(header.name.as_bytes());
    bytes[header.name.len()] = if header.enabled { u8::max_value() } else { 0 };

    Some(())
}

struct Header<'a> {
    name: &'a str,
    enabled: bool,
}

impl<'a> TryRead<'a, Endian> for Header<'a> {
    fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> {
        let offset = &mut 0;

        let name_len = black_box(bytes.read_with::<u16>(offset, endian)? as usize);
        let name = bytes.read_with::<&str>(offset, Str::Len(name_len))?;
        let enabled = bytes.read(offset)?;

        Ok((Header { name, enabled }, *offset))
    }
}

impl<'a> TryWrite<Endian> for Header<'a> {
    fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result<usize> {
        let offset = &mut 0;

        bytes.write_with(offset, self.name.len() as u16, endian)?;
        bytes.write(offset, self.name)?;
        bytes.write(offset, self.enabled)?;

        Ok(*offset)
    }
}