<commit_before>use std;
use std::str;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use curl::easy::Easy;
use futures::Future;
use tokio_curl::Session;
use quick_xml::events::BytesStart;
use quick_xml::events::Event as XmlEvent;
use quick_xml::events::attributes::Attributes;
use quick_xml::reader::Reader as XmlReader;

use errors::*;

pub trait FromXml: Sized {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, start: &BytesStart) -> Result<Self>;
}

fn parse_atom_link<B: std::io::BufRead>(reader: &mut XmlReader<B>, attributes: Attributes) -> Option<String> {
    let mut link_tmp = None;
    let mut is_alternate = true;
    for attribute in attributes {
        match attribute {
            Ok(attribute) => {
                match attribute.key {
                    b"href" => {
                        match attribute.unescape_and_decode_value(reader) {
                            Ok(link) => link_tmp = Some(link),
                            Err(_) => continue,
                        }
                    }
                    b"rel" => {
                        is_alternate = attribute.value == b"alternate";
                    }
                    _ => (),
                }
            }
            Err(_) => continue,
        }
    }
    if is_alternate { link_tmp } else { None }
}

fn skip_element<B: std::io::BufRead>(reader: &mut XmlReader<B>) -> Result<()> {
    let mut buf = Vec::new();
    loop {
        match reader.read_event(&mut buf) {
            Ok(XmlEvent::Start(_)) => {
                skip_element(reader)?;
            }
            Ok(XmlEvent::End(_)) => break,
            Ok(XmlEvent::Eof) => break,
            Err(err) => return Err(err.into()),
            _ => (),
        }
        buf.clear();
    }
    Ok(())
}

impl FromXml for Option<String> {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, _start: &BytesStart) -> Result<Self> {
        let mut buf = Vec::new();
        let mut content: Option<String> = None;
        loop {
            match reader.read_event(&mut buf) {
                Ok(XmlEvent::Start(_)) => {
                    skip_element(reader)?;
                }
                Ok(XmlEvent::Text(ref e)) => {
                    let text = e.unescape_and_decode(&reader)?;
                    content = Some(text);
                }
                Ok(XmlEvent::CData(ref e)) => {
                    let text = e.unescape_and_decode(&reader)?;
                    content = Some(text);
                }
                Ok(XmlEvent::End(_)) => break,
                Ok(XmlEvent::Eof) => break,
                Err(err) => return Err(err.into()),
                _ => (),
            }
            buf.clear();
        }
        Ok(content)
    }
}

#[derive(Debug, Clone, Default)]
pub struct RSS {
    pub title: String,
    pub link: String,
    pub items: Vec<Item>,
}

impl FromXml for RSS {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, _start: &BytesStart) -> Result<Self> {
        let mut buf = Vec::new();
        let mut rss = RSS::default();
        loop {
            match reader.read_event(&mut buf) {
                Ok(XmlEvent::Empty(ref e)) => {
                    match e.name() {
                        b"link" => {
                            if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                rss.link = link;
                            }
                        }
                        _ => (),
                    }
                }
                Ok(XmlEvent::Start(ref e)) => {
                    match e.name() {
                        b"title" => {
                            if let Some(title) = Option::from_xml(reader, e)? {
                                rss.title = title;
                            }
                        }
                        b"link" => {
                            if let Some(link) = Option::from_xml(reader, e)? {
                                // RSS
                                rss.link = link;
                            } else {
                                // ATOM
                                if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                    rss.link = link;
                                }
                            }
                        }
                        b"item" | b"entry" => {
                            rss.items.push(Item::from_xml(reader, e)?);
                        }
                        _ => skip_element(reader)?,
                    }
                }
                Ok(XmlEvent::End(_)) => break,
                Ok(XmlEvent::Eof) => break,
                Err(err) => return Err(err.into()),
                _ => (),
            }
            buf.clear();
        }
        Ok(rss)
    }
}

#[derive(Debug, Clone, Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub id: Option<String>,
}

impl FromXml for Item {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, _start: &BytesStart) -> Result<Self> {
        let mut buf = Vec::new();
        let mut item = Item::default();
        loop {
            match reader.read_event(&mut buf) {
                Ok(XmlEvent::Empty(ref e)) => {
                    match e.name() {
                        b"link" => {
                            if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                item.link = Some(link);
                            }
                        }
                        _ => (),
                    }
                }
                Ok(XmlEvent::Start(ref e)) => {
                    match e.name() {
                        b"title" => {
                            item.title = Option::from_xml(reader, e)?;
                        }
                        b"link" => {
                            if let Some(link) = Option::from_xml(reader, e)? {
                                // RSS
                                item.link = Some(link);
                            } else {
                                // ATOM
                                if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                    item.link = Some(link);
                                }
                            }
                        }
                        b"id" | b"guid" => {
                            item.id = Option::from_xml(reader, e)?;
                        }
                        _ => skip_element(reader)?,
                    }
                }
                Ok(XmlEvent::End(_)) => break,
                Ok(XmlEvent::Eof) => break,
                Err(err) => return Err(err.into()),
                _ => (),
            }
            buf.clear();
        }
        Ok(item)
    }
}

pub fn parse<B: std::io::BufRead>(reader: B) -> Result<RSS> {
    let mut reader = XmlReader::from_reader(reader);
    reader.trim_text(true);
    let mut buf = Vec::new();
    loop {
        match reader.read_event(&mut buf) {
            Ok(XmlEvent::Start(ref e)) => {
                match e.name() {
                    b"rss" => continue,
                    b"channel" | b"feed" => {
                        return RSS::from_xml(&mut reader, e);
                    }
                    _ => skip_element(&mut reader)?,
                }
            }
            Ok(XmlEvent::Eof) => return Err(ErrorKind::EOF.into()),
            Err(err) => return Err(err.into()),
            _ => (),
        }
        buf.clear();
    }
}

pub fn fetch_feed<'a>(session: &Session, link: &str) -> impl Future<Item = RSS, Error = Error> + 'a {
    let mut req = Easy::new();
    let buf = Arc::new(Mutex::new(Vec::new()));
    {
        let buf = buf.clone();
        req.get(true).unwrap();
        req.url(link).unwrap();
        req.accept_encoding("").unwrap(); // accept all encoding
        req.follow_location(true).unwrap();
        req.timeout(Duration::from_secs(10)).unwrap();
        req.write_function(move |data| {
                buf.lock().unwrap().extend_from_slice(data);
                Ok(data.len())
            })
            .unwrap();
    }
    session.perform(req).map_err(|e| e.into()).and_then(move |mut resp| {
        let response_code = resp.response_code().unwrap();
        if response_code != 200 {
            return Err(ErrorKind::Http(response_code).into());
        }
        let buf = buf.lock().unwrap();
        parse(buf.as_slice())
    })
}
<commit_msg>add user agent<commit_after>use std;
use std::str;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use curl::easy::Easy;
use futures::Future;
use tokio_curl::Session;
use quick_xml::events::BytesStart;
use quick_xml::events::Event as XmlEvent;
use quick_xml::events::attributes::Attributes;
use quick_xml::reader::Reader as XmlReader;

use errors::*;

pub trait FromXml: Sized {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, start: &BytesStart) -> Result<Self>;
}

fn parse_atom_link<B: std::io::BufRead>(reader: &mut XmlReader<B>, attributes: Attributes) -> Option<String> {
    let mut link_tmp = None;
    let mut is_alternate = true;
    for attribute in attributes {
        match attribute {
            Ok(attribute) => {
                match attribute.key {
                    b"href" => {
                        match attribute.unescape_and_decode_value(reader) {
                            Ok(link) => link_tmp = Some(link),
                            Err(_) => continue,
                        }
                    }
                    b"rel" => {
                        is_alternate = attribute.value == b"alternate";
                    }
                    _ => (),
                }
            }
            Err(_) => continue,
        }
    }
    if is_alternate { link_tmp } else { None }
}

fn skip_element<B: std::io::BufRead>(reader: &mut XmlReader<B>) -> Result<()> {
    let mut buf = Vec::new();
    loop {
        match reader.read_event(&mut buf) {
            Ok(XmlEvent::Start(_)) => {
                skip_element(reader)?;
            }
            Ok(XmlEvent::End(_)) => break,
            Ok(XmlEvent::Eof) => break,
            Err(err) => return Err(err.into()),
            _ => (),
        }
        buf.clear();
    }
    Ok(())
}

impl FromXml for Option<String> {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, _start: &BytesStart) -> Result<Self> {
        let mut buf = Vec::new();
        let mut content: Option<String> = None;
        loop {
            match reader.read_event(&mut buf) {
                Ok(XmlEvent::Start(_)) => {
                    skip_element(reader)?;
                }
                Ok(XmlEvent::Text(ref e)) => {
                    let text = e.unescape_and_decode(&reader)?;
                    content = Some(text);
                }
                Ok(XmlEvent::CData(ref e)) => {
                    let text = e.unescape_and_decode(&reader)?;
                    content = Some(text);
                }
                Ok(XmlEvent::End(_)) => break,
                Ok(XmlEvent::Eof) => break,
                Err(err) => return Err(err.into()),
                _ => (),
            }
            buf.clear();
        }
        Ok(content)
    }
}

#[derive(Debug, Clone, Default)]
pub struct RSS {
    pub title: String,
    pub link: String,
    pub items: Vec<Item>,
}

impl FromXml for RSS {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, _start: &BytesStart) -> Result<Self> {
        let mut buf = Vec::new();
        let mut rss = RSS::default();
        loop {
            match reader.read_event(&mut buf) {
                Ok(XmlEvent::Empty(ref e)) => {
                    match e.name() {
                        b"link" => {
                            if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                rss.link = link;
                            }
                        }
                        _ => (),
                    }
                }
                Ok(XmlEvent::Start(ref e)) => {
                    match e.name() {
                        b"title" => {
                            if let Some(title) = Option::from_xml(reader, e)? {
                                rss.title = title;
                            }
                        }
                        b"link" => {
                            if let Some(link) = Option::from_xml(reader, e)? {
                                // RSS
                                rss.link = link;
                            } else {
                                // ATOM
                                if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                    rss.link = link;
                                }
                            }
                        }
                        b"item" | b"entry" => {
                            rss.items.push(Item::from_xml(reader, e)?);
                        }
                        _ => skip_element(reader)?,
                    }
                }
                Ok(XmlEvent::End(_)) => break,
                Ok(XmlEvent::Eof) => break,
                Err(err) => return Err(err.into()),
                _ => (),
            }
            buf.clear();
        }
        Ok(rss)
    }
}

#[derive(Debug, Clone, Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub id: Option<String>,
}

impl FromXml for Item {
    fn from_xml<B: std::io::BufRead>(reader: &mut XmlReader<B>, _start: &BytesStart) -> Result<Self> {
        let mut buf = Vec::new();
        let mut item = Item::default();
        loop {
            match reader.read_event(&mut buf) {
                Ok(XmlEvent::Empty(ref e)) => {
                    match e.name() {
                        b"link" => {
                            if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                item.link = Some(link);
                            }
                        }
                        _ => (),
                    }
                }
                Ok(XmlEvent::Start(ref e)) => {
                    match e.name() {
                        b"title" => {
                            item.title = Option::from_xml(reader, e)?;
                        }
                        b"link" => {
                            if let Some(link) = Option::from_xml(reader, e)? {
                                // RSS
                                item.link = Some(link);
                            } else {
                                // ATOM
                                if let Some(link) = parse_atom_link(reader, e.attributes()) {
                                    item.link = Some(link);
                                }
                            }
                        }
                        b"id" | b"guid" => {
                            item.id = Option::from_xml(reader, e)?;
                        }
                        _ => skip_element(reader)?,
                    }
                }
                Ok(XmlEvent::End(_)) => break,
                Ok(XmlEvent::Eof) => break,
                Err(err) => return Err(err.into()),
                _ => (),
            }
            buf.clear();
        }
        Ok(item)
    }
}

pub fn parse<B: std::io::BufRead>(reader: B) -> Result<RSS> {
    let mut reader = XmlReader::from_reader(reader);
    reader.trim_text(true);
    let mut buf = Vec::new();
    loop {
        match reader.read_event(&mut buf) {
            Ok(XmlEvent::Start(ref e)) => {
                match e.name() {
                    b"rss" => continue,
                    b"channel" | b"feed" => {
                        return RSS::from_xml(&mut reader, e);
                    }
                    _ => skip_element(&mut reader)?,
                }
            }
            Ok(XmlEvent::Eof) => return Err(ErrorKind::EOF.into()),
            Err(err) => return Err(err.into()),
            _ => (),
        }
        buf.clear();
    }
}

pub fn fetch_feed<'a>(session: &Session, link: &str) -> impl Future<Item = RSS, Error = Error> + 'a {
    let mut req = Easy::new();
    let buf = Arc::new(Mutex::new(Vec::new()));
    {
        let buf = buf.clone();
        req.get(true).unwrap();
        req.url(link).unwrap();
        req.accept_encoding("").unwrap(); // accept all encoding
        req.useragent("RSSBot/1.0 (https://github.com/iovxw/rssbot)").unwrap();
        req.follow_location(true).unwrap();
        req.timeout(Duration::from_secs(10)).unwrap();
        req.write_function(move |data| {
                buf.lock().unwrap().extend_from_slice(data);
                Ok(data.len())
            })
            .unwrap();
    }
    session.perform(req).map_err(|e| e.into()).and_then(move |mut resp| {
        let response_code = resp.response_code().unwrap();
        if response_code != 200 {
            return Err(ErrorKind::Http(response_code).into());
        }
        let buf = buf.lock().unwrap();
        parse(buf.as_slice())
    })
}
<|endoftext|>


<commit_before>use std::sync::Mutex;
use std::path::Path;
use std::f32;

use img;

use {clamp, Point2f, Point2i, Vector2f};
use bounds::{Bounds2f, Bounds2i};
use errors::*;
use filter::Filter;
use paramset::ParamSet;
use spectrum::Spectrum;

const FILTER_SIZE: usize = 16;
const FILTER_TABLE_SIZE: usize = FILTER_SIZE * FILTER_SIZE;

#[derive(Copy, Clone, Debug, Default)]
pub struct PixelSample {
    pub c: Spectrum,
    pub weighted_sum: f32,
}

impl PixelSample {
    pub fn render(&self) -> Spectrum {
        if self.weighted_sum == 0.0 {
            Spectrum::black()
        } else {
            self.c / self.weighted_sum
        }
    }
}

pub struct Film {
    pub full_resolution: Point2i,
    samples: Mutex<Vec<PixelSample>>,
    filter_table: [f32; FILTER_TABLE_SIZE],
    filter_radius: Vector2f,
    cropped_pixel_bounds: Bounds2i,
    scale: f32,
    diagonal: f32,
    filename: String,
}

impl Film {
    pub fn new(
        resolution: Point2i,
        cropwindow: Bounds2f,
        filter: Box<Filter + Sync + Send>,
        diagonal: f32,
        filename: &str,
        scale: f32,
    ) -> Film {
        let cropped_pixel_bounds = Bounds2i::from_points(
            &Point2i::new(
                (resolution.x as f32 * cropwindow.p_min.x).ceil() as i32,
                (resolution.y as f32 * cropwindow.p_min.y).ceil() as i32,
            ),
            &Point2i::new(
                (resolution.x as f32 * cropwindow.p_max.x).ceil() as i32,
                (resolution.y as f32 * cropwindow.p_max.y).ceil() as i32,
            ),
        );

        let mut samples = Vec::with_capacity(cropped_pixel_bounds.area() as usize);
        samples.resize(cropped_pixel_bounds.area() as usize, PixelSample::default());
        let mut filter_table = [0f32; FILTER_TABLE_SIZE];

        let (xwidth, ywidth) = filter.width();
        // Fill in filter table
        for y in 0..FILTER_SIZE {
            let fy = (y as f32 + 0.5) * (ywidth / FILTER_SIZE as f32);
            for x in 0..FILTER_SIZE {
                let fx = (x as f32 + 0.5) * (xwidth / FILTER_SIZE as f32);
                filter_table[y * FILTER_SIZE + x] = filter.evaluate(fx, fy);
            }
        }

        Film {
            full_resolution: resolution,
            samples: Mutex::new(samples),
            filter_table: filter_table,
            filter_radius: Vector2f::new(xwidth, ywidth),
            cropped_pixel_bounds: cropped_pixel_bounds,
            scale: scale,
            diagonal: diagonal * 0.001,
            filename: filename.to_owned(),
        }
    }

    pub fn create(ps: &mut ParamSet, filter: Box<Filter + Send + Sync>) -> Box<Film> {
        // TODO filename
        let filename = "image.png";
        let xres = ps.find_one_int("xresolution", 1280);
        let yres = ps.find_one_int("yresolution", 720);
        let mut crop = Bounds2f::from_points(&Point2f::new(0.0, 0.0), &Point2f::new(1.0, 1.0));
        if let Some(cr) = ps.find_float("cropwindow") {
            if cr.len() == 4 {
                crop.p_min.x = clamp(f32::min(cr[0], cr[1]), 0.0, 1.0);
                crop.p_max.x = clamp(f32::max(cr[0], cr[1]), 0.0, 1.0);
                crop.p_min.y = clamp(f32::min(cr[2], cr[3]), 0.0, 1.0);
                crop.p_max.y = clamp(f32::max(cr[2], cr[3]), 0.0, 1.0);
            } else {
                warn!("\"cropwindow\" expected 4 values");
            }
        }
        let scale = ps.find_one_float("scale", 1.0);
        let diagonal = ps.find_one_float("diagonal", 35.0);
        // TODO max_sample_luminance
        Box::new(Film::new(
            Point2i::new(xres, yres),
            crop,
            filter,
            diagonal,
            filename,
            scale,
        ))
    }

    pub fn get_film_tile(&self, sample_bounds: &Bounds2i) -> FilmTile {
        let half_pixel = Vector2f::new(0.5, 0.5);
        let float_bounds: Bounds2f = (*sample_bounds).into();
        let float_cropped_pixel_bounds: Bounds2f = self.cropped_pixel_bounds.into();

        // This is a bit clunky but we need to do all the computations as floats as the numbers can
        // temporarily be negative which would cause u32 to wrap around.
        let p0 = ceil(float_bounds.p_min - half_pixel - self.filter_radius);
        let p1 =
            floor(float_bounds.p_max - half_pixel - self.filter_radius + Vector2f::new(1.0, 1.0));
        let sample_extent_bounds = Bounds2f::from_points(&p0, &p1);

        let tile_pixel_bounds: Bounds2i = Bounds2i::from(Bounds2f::intersect(
            &sample_extent_bounds,
            &float_cropped_pixel_bounds,
        ));

        FilmTile::new(&tile_pixel_bounds, &self.filter_radius, &self.filter_table)
    }

    pub fn merge_film_tile(&self, tile: FilmTile) {
        let mut samples = self.samples.lock().unwrap();
        for p in &tile.get_pixel_bounds() {
            let pixel = tile.get_pixel(&p);
            let pidx = (p.y * self.full_resolution.x + p.x) as usize;
            samples[pidx].c += pixel.contrib_sum;
            samples[pidx].weighted_sum += pixel.filter_weight_sum;
        }
    }

    pub fn render(&self) -> Vec<Spectrum> {
        let samples = self.samples.lock().unwrap();
        samples.iter().map(|s| s.render()).collect()
    }

    pub fn write_png(&self) -> Result<()> {
        let mut buffer = Vec::new();
        let image = self.render();
        let res = self.cropped_pixel_bounds.diagonal();

        info!("Converting image to sRGB");
        for i in 0..self.cropped_pixel_bounds.area() {
            let bytes = image[i as usize].to_srgb();
            buffer.push(bytes[0]);
            buffer.push(bytes[1]);
            buffer.push(bytes[2]);
        }

        // Save the buffer
        info!("Writing image to file {}", self.filename);
        img::save_buffer(
            &Path::new(self.filename.as_str()),
            &buffer,
            res.x as u32,
            res.y as u32,
            img::RGB(8),
        ).chain_err(|| format!("Failed to save image file {}", self.filename))
    }
}

pub struct FilmTile {
    pixel_bounds: Bounds2i,
    filter_radius: Vector2f,
    inv_filter_radius: Vector2f,
    filter_table: Box<[f32]>,
    pub pixels: Vec<FilmTilePixel>,
}

impl FilmTile {
    pub fn new(pixel_bounds: &Bounds2i, filter_radius: &Vector2f, filter: &[f32]) -> FilmTile {
        let mut filter_table = Vec::new();
        filter_table.extend_from_slice(filter);
        FilmTile {
            pixel_bounds: *pixel_bounds,
            filter_radius: *filter_radius,
            inv_filter_radius: Vector2f::new(1.0 / filter_radius.x, 1.0 / filter_radius.y),
            // Duplicating the filter table in every table is wasteful, but keeping a reference to
            // the data from Film leads to all kind of lifetime issues...
            filter_table: filter_table.into_boxed_slice(),
            pixels: vec![FilmTilePixel::default(); pixel_bounds.area() as usize],
        }
    }

    pub fn add_sample(&mut self, p_film: &Point2f, colour: Spectrum) {
        if colour.has_nan() {
            warn!("colour has NaNs! Ignoring");
            return;
        }
        let float_pixel_bounds: Bounds2f = self.pixel_bounds.into();
        // Convert to discrete pixel space
        let p_film_discrete = *p_film - Vector2f::new(0.5, 0.5);
        // compute sample raster extent (i.e. how many pixels are affected)
        // (x0, y0) -> (x1, y1) is the zone of the image affected by the sample
        let p0_f = ceil(p_film_discrete - self.filter_radius);

        let p1_f = floor(p_film_discrete + self.filter_radius + Vector2f::new(1.0, 1.0));

        let bounds: Bounds2i = Bounds2i::from(Bounds2f::intersect(
            &Bounds2f::from_points(&p0_f, &p1_f),
            &float_pixel_bounds,
        ));
        let (p0, p1) = (bounds.p_min, bounds.p_max);

        assert!(
            p1.x >= p0.x && p1.y >= p0.y,
            format!(
                "p_film={}, p0={}, p1={}, pixel_bounds={:?}",
                p_film,
                p0,
                p1,
                self.pixel_bounds
            )
        );

        let filter_table_size = FILTER_SIZE as f32;

        // Precompute x and y filter table offset
        let mut ifx = Vec::with_capacity(p1.x as usize - p0.x as usize);
        for x in p0.x..p1.x {
            let fx = ((x as f32 - p_film_discrete.x) * self.inv_filter_radius.x
                * filter_table_size)
                .abs();
            ifx.push(fx.floor().min(filter_table_size - 1.0) as usize);
        }
        let mut ify = Vec::with_capacity(p1.y as usize - p0.y as usize);
        for y in p0.y..p1.y {
            let fy = ((y as f32 - p_film_discrete.y) * self.inv_filter_radius.y
                * filter_table_size)
                .abs();
            ify.push(fy.floor().min(filter_table_size - 1.0) as usize);
        }

        // Add this sample's contribution to all the affected pixels
        for y in p0.y..p1.y {
            for x in p0.x..p1.x {
                let offset = ify[(y - p0.y) as usize] * FILTER_SIZE + ifx[(x - p0.x) as usize];
                let filter_weight = &self.filter_table[offset];
                let idx = self.get_pixel_index(&Point2i::new(x, y));
                let pixel = &mut self.pixels[idx];
                pixel.contrib_sum += colour * *filter_weight;
                pixel.filter_weight_sum += *filter_weight;
            }
        }
    }

    pub fn get_pixel<'a>(&'a self, p: &Point2i) -> &'a FilmTilePixel {
        &self.pixels[self.get_pixel_index(p)]
    }

    pub fn get_pixel_bounds(&self) -> Bounds2i {
        self.pixel_bounds
    }

    fn get_pixel_index(&self, p: &Point2i) -> usize {
        let width = self.pixel_bounds.p_max.x - self.pixel_bounds.p_min.x;
        let pidx = (p.y - self.pixel_bounds.p_min.y) * width + (p.x - self.pixel_bounds.p_min.x);
        pidx as usize
    }
}

#[derive(Clone, Default)]
pub struct FilmTilePixel {
    contrib_sum: Spectrum,
    filter_weight_sum: f32,
}

fn ceil(p: Point2f) -> Point2f {
    Point2f::new(p.x.ceil(), p.y.ceil())
}

fn floor(p: Point2f) -> Point2f {
    Point2f::new(p.x.floor(), p.y.floor())
}
<commit_msg>Honour filename from scene file but prepend "rt-"<commit_after>use std::sync::Mutex;
use std::path::Path;
use std::f32;

use img;

use {clamp, Point2f, Point2i, Vector2f};
use bounds::{Bounds2f, Bounds2i};
use errors::*;
use filter::Filter;
use paramset::ParamSet;
use spectrum::Spectrum;

const FILTER_SIZE: usize = 16;
const FILTER_TABLE_SIZE: usize = FILTER_SIZE * FILTER_SIZE;

#[derive(Copy, Clone, Debug, Default)]
pub struct PixelSample {
    pub c: Spectrum,
    pub weighted_sum: f32,
}

impl PixelSample {
    pub fn render(&self) -> Spectrum {
        if self.weighted_sum == 0.0 {
            Spectrum::black()
        } else {
            self.c / self.weighted_sum
        }
    }
}

pub struct Film {
    pub full_resolution: Point2i,
    samples: Mutex<Vec<PixelSample>>,
    filter_table: [f32; FILTER_TABLE_SIZE],
    filter_radius: Vector2f,
    cropped_pixel_bounds: Bounds2i,
    scale: f32,
    diagonal: f32,
    filename: String,
}

impl Film {
    pub fn new(
        resolution: Point2i,
        cropwindow: Bounds2f,
        filter: Box<Filter + Sync + Send>,
        diagonal: f32,
        filename: &str,
        scale: f32,
    ) -> Film {
        let cropped_pixel_bounds = Bounds2i::from_points(
            &Point2i::new(
                (resolution.x as f32 * cropwindow.p_min.x).ceil() as i32,
                (resolution.y as f32 * cropwindow.p_min.y).ceil() as i32,
            ),
            &Point2i::new(
                (resolution.x as f32 * cropwindow.p_max.x).ceil() as i32,
                (resolution.y as f32 * cropwindow.p_max.y).ceil() as i32,
            ),
        );

        let mut samples = Vec::with_capacity(cropped_pixel_bounds.area() as usize);
        samples.resize(cropped_pixel_bounds.area() as usize, PixelSample::default());
        let mut filter_table = [0f32; FILTER_TABLE_SIZE];

        let (xwidth, ywidth) = filter.width();
        // Fill in filter table
        for y in 0..FILTER_SIZE {
            let fy = (y as f32 + 0.5) * (ywidth / FILTER_SIZE as f32);
            for x in 0..FILTER_SIZE {
                let fx = (x as f32 + 0.5) * (xwidth / FILTER_SIZE as f32);
                filter_table[y * FILTER_SIZE + x] = filter.evaluate(fx, fy);
            }
        }

        Film {
            full_resolution: resolution,
            samples: Mutex::new(samples),
            filter_table: filter_table,
            filter_radius: Vector2f::new(xwidth, ywidth),
            cropped_pixel_bounds: cropped_pixel_bounds,
            scale: scale,
            diagonal: diagonal * 0.001,
            filename: filename.to_owned(),
        }
    }

    pub fn create(ps: &mut ParamSet, filter: Box<Filter + Send + Sync>) -> Box<Film> {
        // TODO filename
        let mut filename = ps.find_one_string("filename", "".into());
        if filename == "" {
            filename = "image.png".into();
        } else {
            filename = String::from("rt-") + &filename;
        }
        let xres = ps.find_one_int("xresolution", 1280);
        let yres = ps.find_one_int("yresolution", 720);
        let mut crop = Bounds2f::from_points(&Point2f::new(0.0, 0.0), &Point2f::new(1.0, 1.0));
        if let Some(cr) = ps.find_float("cropwindow") {
            if cr.len() == 4 {
                crop.p_min.x = clamp(f32::min(cr[0], cr[1]), 0.0, 1.0);
                crop.p_max.x = clamp(f32::max(cr[0], cr[1]), 0.0, 1.0);
                crop.p_min.y = clamp(f32::min(cr[2], cr[3]), 0.0, 1.0);
                crop.p_max.y = clamp(f32::max(cr[2], cr[3]), 0.0, 1.0);
            } else {
                warn!("\"cropwindow\" expected 4 values");
            }
        }
        let scale = ps.find_one_float("scale", 1.0);
        let diagonal = ps.find_one_float("diagonal", 35.0);
        // TODO max_sample_luminance
        Box::new(Film::new(
            Point2i::new(xres, yres),
            crop,
            filter,
            diagonal,
            &filename,
            scale,
        ))
    }

    pub fn get_film_tile(&self, sample_bounds: &Bounds2i) -> FilmTile {
        let half_pixel = Vector2f::new(0.5, 0.5);
        let float_bounds: Bounds2f = (*sample_bounds).into();
        let float_cropped_pixel_bounds: Bounds2f = self.cropped_pixel_bounds.into();

        // This is a bit clunky but we need to do all the computations as floats as the numbers can
        // temporarily be negative which would cause u32 to wrap around.
        let p0 = ceil(float_bounds.p_min - half_pixel - self.filter_radius);
        let p1 =
            floor(float_bounds.p_max - half_pixel - self.filter_radius + Vector2f::new(1.0, 1.0));
        let sample_extent_bounds = Bounds2f::from_points(&p0, &p1);

        let tile_pixel_bounds: Bounds2i = Bounds2i::from(Bounds2f::intersect(
            &sample_extent_bounds,
            &float_cropped_pixel_bounds,
        ));

        FilmTile::new(&tile_pixel_bounds, &self.filter_radius, &self.filter_table)
    }

    pub fn merge_film_tile(&self, tile: FilmTile) {
        let mut samples = self.samples.lock().unwrap();
        for p in &tile.get_pixel_bounds() {
            let pixel = tile.get_pixel(&p);
            let pidx = (p.y * self.full_resolution.x + p.x) as usize;
            samples[pidx].c += pixel.contrib_sum;
            samples[pidx].weighted_sum += pixel.filter_weight_sum;
        }
    }

    pub fn render(&self) -> Vec<Spectrum> {
        let samples = self.samples.lock().unwrap();
        samples.iter().map(|s| s.render()).collect()
    }

    pub fn write_png(&self) -> Result<()> {
        let mut buffer = Vec::new();
        let image = self.render();
        let res = self.cropped_pixel_bounds.diagonal();

        info!("Converting image to sRGB");
        for i in 0..self.cropped_pixel_bounds.area() {
            let bytes = image[i as usize].to_srgb();
            buffer.push(bytes[0]);
            buffer.push(bytes[1]);
            buffer.push(bytes[2]);
        }

        // Save the buffer
        info!("Writing image to file {}", self.filename);
        img::save_buffer(
            &Path::new(self.filename.as_str()),
            &buffer,
            res.x as u32,
            res.y as u32,
            img::RGB(8),
        ).chain_err(|| format!("Failed to save image file {}", self.filename))
    }
}

pub struct FilmTile {
    pixel_bounds: Bounds2i,
    filter_radius: Vector2f,
    inv_filter_radius: Vector2f,
    filter_table: Box<[f32]>,
    pub pixels: Vec<FilmTilePixel>,
}

impl FilmTile {
    pub fn new(pixel_bounds: &Bounds2i, filter_radius: &Vector2f, filter: &[f32]) -> FilmTile {
        let mut filter_table = Vec::new();
        filter_table.extend_from_slice(filter);
        FilmTile {
            pixel_bounds: *pixel_bounds,
            filter_radius: *filter_radius,
            inv_filter_radius: Vector2f::new(1.0 / filter_radius.x, 1.0 / filter_radius.y),
            // Duplicating the filter table in every table is wasteful, but keeping a reference to
            // the data from Film leads to all kind of lifetime issues...
            filter_table: filter_table.into_boxed_slice(),
            pixels: vec![FilmTilePixel::default(); pixel_bounds.area() as usize],
        }
    }

    pub fn add_sample(&mut self, p_film: &Point2f, colour: Spectrum) {
        if colour.has_nan() {
            warn!("colour has NaNs! Ignoring");
            return;
        }
        let float_pixel_bounds: Bounds2f = self.pixel_bounds.into();
        // Convert to discrete pixel space
        let p_film_discrete = *p_film - Vector2f::new(0.5, 0.5);
        // compute sample raster extent (i.e. how many pixels are affected)
        // (x0, y0) -> (x1, y1) is the zone of the image affected by the sample
        let p0_f = ceil(p_film_discrete - self.filter_radius);

        let p1_f = floor(p_film_discrete + self.filter_radius + Vector2f::new(1.0, 1.0));

        let bounds: Bounds2i = Bounds2i::from(Bounds2f::intersect(
            &Bounds2f::from_points(&p0_f, &p1_f),
            &float_pixel_bounds,
        ));
        let (p0, p1) = (bounds.p_min, bounds.p_max);

        assert!(
            p1.x >= p0.x && p1.y >= p0.y,
            format!(
                "p_film={}, p0={}, p1={}, pixel_bounds={:?}",
                p_film,
                p0,
                p1,
                self.pixel_bounds
            )
        );

        let filter_table_size = FILTER_SIZE as f32;

        // Precompute x and y filter table offset
        let mut ifx = Vec::with_capacity(p1.x as usize - p0.x as usize);
        for x in p0.x..p1.x {
            let fx = ((x as f32 - p_film_discrete.x) * self.inv_filter_radius.x
                * filter_table_size)
                .abs();
            ifx.push(fx.floor().min(filter_table_size - 1.0) as usize);
        }
        let mut ify = Vec::with_capacity(p1.y as usize - p0.y as usize);
        for y in p0.y..p1.y {
            let fy = ((y as f32 - p_film_discrete.y) * self.inv_filter_radius.y
                * filter_table_size)
                .abs();
            ify.push(fy.floor().min(filter_table_size - 1.0) as usize);
        }

        // Add this sample's contribution to all the affected pixels
        for y in p0.y..p1.y {
            for x in p0.x..p1.x {
                let offset = ify[(y - p0.y) as usize] * FILTER_SIZE + ifx[(x - p0.x) as usize];
                let filter_weight = &self.filter_table[offset];
                let idx = self.get_pixel_index(&Point2i::new(x, y));
                let pixel = &mut self.pixels[idx];
                pixel.contrib_sum += colour * *filter_weight;
                pixel.filter_weight_sum += *filter_weight;
            }
        }
    }

    pub fn get_pixel<'a>(&'a self, p: &Point2i) -> &'a FilmTilePixel {
        &self.pixels[self.get_pixel_index(p)]
    }

    pub fn get_pixel_bounds(&self) -> Bounds2i {
        self.pixel_bounds
    }

    fn get_pixel_index(&self, p: &Point2i) -> usize {
        let width = self.pixel_bounds.p_max.x - self.pixel_bounds.p_min.x;
        let pidx = (p.y - self.pixel_bounds.p_min.y) * width + (p.x - self.pixel_bounds.p_min.x);
        pidx as usize
    }
}

#[derive(Clone, Default)]
pub struct FilmTilePixel {
    contrib_sum: Spectrum,
    filter_weight_sum: f32,
}

fn ceil(p: Point2f) -> Point2f {
    Point2f::new(p.x.ceil(), p.y.ceil())
}

fn floor(p: Point2f) -> Point2f {
    Point2f::new(p.x.floor(), p.y.floor())
}
<|endoftext|>


<commit_before>use super::cell::*;
use super::fluid::*;
use itertools::Itertools;
use std::mem;
use rand::{Isaac64Rng, Rng};
use noise::{Brownian2, perlin2};

const SPAWN_RATE: f64 = 0.1;
const CONSUMPTION: f64 = 0.002;
const SURVIVAL_THRESHOLD: f64 = 0.1;
const INHALE_CAP: usize = 20;

#[derive(Debug, Clone)]
struct Mate {
    mate: (usize, usize),
    source: (usize, usize),
}

#[derive(Debug)]
struct Delta {
    movement_attempts: Vec<(usize, usize)>,
    mate_attempts: Vec<Mate>,
}

pub struct Hex {
    pub solution: Solution,
    pub cell: Option<Cell>,
    pub decision: Option<Decision>,
    delta: Delta,
}

impl Hex {
    pub fn color(&self) -> [f32; 4] {
        [0.25 * self.solution.fluids[1] as f32, 0.0, 0.25 * self.solution.fluids[0] as f32, 1.0]
    }
}

pub struct Grid {
    pub spawning: bool,
    pub width: usize,
    pub height: usize,
    tiles: Vec<Hex>,
}

impl Grid {
    pub fn new(width: usize, height: usize, rng: &mut Isaac64Rng) -> Self {
        Grid {
            spawning: true,
            width: width,
            height: height,
            tiles: randomizing_vec(width, height, rng),
        }
    }

    pub fn randomize(&mut self, rng: &mut Isaac64Rng) {
        self.tiles = randomizing_vec(self.width, self.height, rng);
    }

    pub fn hex(&self, x: usize, y: usize) -> &Hex {
        &self.tiles[x + y * self.width]
    }

    pub fn hex_mut(&mut self, x: usize, y: usize) -> &mut Hex {
        &mut self.tiles[x + y * self.width]
    }

    fn hex_and_neighbors(&mut self, x: usize, y: usize) -> (&mut Hex, [&Hex; 6]) {
        (unsafe { mem::transmute(self.hex_mut(x, y)) },
         if y % 2 == 0 {
            [// UpRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height - 1) % self.height),
             // UpLeft
             self.hex(x, (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex(x, (y + self.height + 1) % self.height),
             // DownRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        } else {
            [// UpRight
             self.hex(x, (y + self.height - 1) % self.height),
             // UpLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height + 1) % self.height),
             // DownRight
             self.hex(x, (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        })
    }

    pub fn cycle(&mut self, rng: &mut Isaac64Rng) {
        if self.spawning {
            self.cycle_spawn(rng);
        }
        self.cycle_cells();
        self.cycle_decisions(rng);
        self.cycle_fluids();
        self.cycle_death();
    }

    fn cycle_spawn(&mut self, rng: &mut Isaac64Rng) {
        if rng.next_f64() < SPAWN_RATE {
            self.tiles[rng.gen_range(0, self.width * self.height)].cell = Some(Cell::new(rng));
        }
    }

    fn cycle_cells(&mut self) {
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                this.decision = if let Some(ref mut this_cell) = this.cell {
                    let neighbor_presents = [neighbors[0].cell.is_some(),
                                             neighbors[1].cell.is_some(),
                                             neighbors[2].cell.is_some(),
                                             neighbors[3].cell.is_some(),
                                             neighbors[4].cell.is_some(),
                                             neighbors[5].cell.is_some()];

                    Some(this_cell.decide([&this.solution.fluids,
                                           &neighbors[0].solution.fluids,
                                           &neighbors[1].solution.fluids,
                                           &neighbors[2].solution.fluids,
                                           &neighbors[3].solution.fluids,
                                           &neighbors[4].solution.fluids,
                                           &neighbors[5].solution.fluids],
                                          &neighbor_presents))
                } else {
                    None
                }
            }
        }
    }

    fn cycle_decisions(&mut self, rng: &mut Isaac64Rng) {
        // Compute the deltas resulting from the decision.
        for x in 0..self.width {
            for y in 0..self.height {
                let (width, height) = (self.width, self.height);
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                // Clear the movements from the previous cycle.
                this.delta.movement_attempts.clear();
                this.delta.mate_attempts.clear();
                this.solution.coefficients = if let Some(ref decision) = this.decision {
                    decision.coefficients
                } else {
                    // Set the diffusion coefficients to the normal values.
                    [0.5, 1.0]
                };

                // Only add movements here if no cell is present.
                if this.cell.is_none() {
                    // Add any neighbor movements to the movement_attempts vector.
                    for (n, &facing) in neighbors.iter().zip(&[Direction::DownLeft,
                                                               Direction::DownRight,
                                                               Direction::Right,
                                                               Direction::UpRight,
                                                               Direction::UpLeft,
                                                               Direction::Left]) {
                        match n.decision {
                            Some(Decision { choice: Choice::Move(direction), .. }) => {
                                // It attempted to move into this hex cell.
                                if facing == direction {
                                    this.delta
                                        .movement_attempts
                                        .push(in_direction(x, y, width, height, facing.flip()));

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.movement_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            Some(Decision { choice: Choice::Divide { mate, spawn }, .. }) => {
                                // It attempted to spawn into this hex cell.
                                if facing == spawn {
                                    let source = in_direction(x, y, width, height, facing.flip());;
                                    this.delta
                                        .mate_attempts
                                        .push(Mate {
                                            mate: in_direction(source.0,
                                                               source.1,
                                                               width,
                                                               height,
                                                               mate),
                                            source: source,
                                        });

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.mate_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        // Perform the deltas.
        for x in 0..self.width {
            for y in 0..self.height {
                // Handle movement.
                if self.hex(x, y).delta.movement_attempts.len() == 1 {
                    let from_coord = self.hex(x, y).delta.movement_attempts[0];
                    self.hex_mut(x, y).cell = self.hex_mut(from_coord.0, from_coord.1).cell.take();
                    // Handle mating.
                } else if self.hex(x, y).delta.mate_attempts.len() == 1 {
                    let mate = self.hex(x, y).delta.mate_attempts[0].clone();
                    self.hex_mut(x, y).cell = if mate.mate == (x, y) {
                        Some(self.hex(mate.source.0, mate.source.1)
                            .cell
                            .as_ref()
                            .unwrap()
                            .divide(rng))
                    } else {
                        if self.hex(mate.mate.0, mate.mate.1).cell.is_some() {
                            Some(self.hex(mate.source.0, mate.source.1)
                                .cell
                                .as_ref()
                                .unwrap()
                                .mate(&self.hex(mate.mate.0, mate.mate.1)
                                          .cell
                                          .as_ref()
                                          .unwrap(),
                                      rng))
                        } else {
                            None
                        }
                    };
                }

                // Clear the decisions.
                self.hex_mut(x, y).decision = None;
            }
        }
    }

    fn cycle_fluids(&mut self) {
        // Then update diffusion.
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);

                for n in &neighbors {
                    this.solution.diffuse_from(&n.solution);
                }
            }
        }

        // Finish the cycle.
        for hex in &mut self.tiles {
            hex.solution.end_cycle();
        }
    }

    fn cycle_death(&mut self) {
        // Finish the cycle.
        for hex in &mut self.tiles {
            if hex.cell.is_some() {
                if hex.solution.fluids[0] <= CONSUMPTION {
                    if hex.cell.as_ref().unwrap().inhale != 0 {
                        hex.cell.as_mut().unwrap().inhale -= 1;
                    } else {
                        hex.cell = None;
                    }
                } else {
                    hex.solution.fluids[0] -= CONSUMPTION;
                    if hex.solution.fluids[0] < SURVIVAL_THRESHOLD {
                        if hex.cell.as_ref().unwrap().inhale != 0 {
                            hex.cell.as_mut().unwrap().inhale -= 1;
                        } else {
                            hex.cell = None;
                        }
                    } else {
                        if hex.cell.as_ref().unwrap().inhale < INHALE_CAP {
                            hex.cell.as_mut().unwrap().inhale += 1;
                        }
                    }
                }
            }
        }
    }
}

fn randomizing_vec(width: usize, height: usize, rng: &mut Isaac64Rng) -> Vec<Hex> {
    let seeds = [rng.gen(), rng.gen()];
    let noise = Brownian2::new(perlin2, 4).wavelength(32.0);
    (0..height)
        .cartesian_product((0..width))
        .map(|(x, y)| {
            Hex {
                solution: Solution::new([noise.apply(&seeds[0], &[x as f64, y as f64]), 1.0],
                                        [0.5, 1.0]),
                cell: None,
                decision: None,
                delta: Delta {
                    movement_attempts: Vec::with_capacity(6),
                    mate_attempts: Vec::with_capacity(6),
                },
            }
        })
        .collect_vec()
}

fn in_direction(x: usize,
                y: usize,
                width: usize,
                height: usize,
                direction: Direction)
                -> (usize, usize) {
    let diff = direction.delta(y % 2 == 0);
    (((width + x) as isize + diff.0) as usize % width,
     ((height + y) as isize + diff.1) as usize % height)
}
<commit_msg>fix issue with cells getting replaced<commit_after>use super::cell::*;
use super::fluid::*;
use itertools::Itertools;
use std::mem;
use rand::{Isaac64Rng, Rng};
use noise::{Brownian2, perlin2};

const SPAWN_RATE: f64 = 0.1;
const CONSUMPTION: f64 = 0.002;
const SURVIVAL_THRESHOLD: f64 = 0.1;
const INHALE_CAP: usize = 20;

#[derive(Debug, Clone)]
struct Mate {
    mate: (usize, usize),
    source: (usize, usize),
}

#[derive(Debug)]
struct Delta {
    movement_attempts: Vec<(usize, usize)>,
    mate_attempts: Vec<Mate>,
}

pub struct Hex {
    pub solution: Solution,
    pub cell: Option<Cell>,
    pub decision: Option<Decision>,
    delta: Delta,
}

impl Hex {
    pub fn color(&self) -> [f32; 4] {
        [0.25 * self.solution.fluids[1] as f32, 0.0, 0.25 * self.solution.fluids[0] as f32, 1.0]
    }
}

pub struct Grid {
    pub spawning: bool,
    pub width: usize,
    pub height: usize,
    tiles: Vec<Hex>,
}

impl Grid {
    pub fn new(width: usize, height: usize, rng: &mut Isaac64Rng) -> Self {
        Grid {
            spawning: true,
            width: width,
            height: height,
            tiles: randomizing_vec(width, height, rng),
        }
    }

    pub fn randomize(&mut self, rng: &mut Isaac64Rng) {
        self.tiles = randomizing_vec(self.width, self.height, rng);
    }

    pub fn hex(&self, x: usize, y: usize) -> &Hex {
        &self.tiles[x + y * self.width]
    }

    pub fn hex_mut(&mut self, x: usize, y: usize) -> &mut Hex {
        &mut self.tiles[x + y * self.width]
    }

    fn hex_and_neighbors(&mut self, x: usize, y: usize) -> (&mut Hex, [&Hex; 6]) {
        (unsafe { mem::transmute(self.hex_mut(x, y)) },
         if y % 2 == 0 {
            [// UpRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height - 1) % self.height),
             // UpLeft
             self.hex(x, (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex(x, (y + self.height + 1) % self.height),
             // DownRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        } else {
            [// UpRight
             self.hex(x, (y + self.height - 1) % self.height),
             // UpLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height + 1) % self.height),
             // DownRight
             self.hex(x, (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        })
    }

    pub fn cycle(&mut self, rng: &mut Isaac64Rng) {
        if self.spawning {
            self.cycle_spawn(rng);
        }
        self.cycle_cells();
        self.cycle_decisions(rng);
        self.cycle_fluids();
        self.cycle_death();
    }

    fn cycle_spawn(&mut self, rng: &mut Isaac64Rng) {
        if rng.next_f64() < SPAWN_RATE {
            let tile = rng.gen_range(0, self.width * self.height);
            if self.tiles[tile].cell.is_none() {
                self.tiles[tile].cell = Some(Cell::new(rng));
            }
        }
    }

    fn cycle_cells(&mut self) {
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                this.decision = if let Some(ref mut this_cell) = this.cell {
                    let neighbor_presents = [neighbors[0].cell.is_some(),
                                             neighbors[1].cell.is_some(),
                                             neighbors[2].cell.is_some(),
                                             neighbors[3].cell.is_some(),
                                             neighbors[4].cell.is_some(),
                                             neighbors[5].cell.is_some()];

                    Some(this_cell.decide([&this.solution.fluids,
                                           &neighbors[0].solution.fluids,
                                           &neighbors[1].solution.fluids,
                                           &neighbors[2].solution.fluids,
                                           &neighbors[3].solution.fluids,
                                           &neighbors[4].solution.fluids,
                                           &neighbors[5].solution.fluids],
                                          &neighbor_presents))
                } else {
                    None
                }
            }
        }
    }

    fn cycle_decisions(&mut self, rng: &mut Isaac64Rng) {
        // Compute the deltas resulting from the decision.
        for x in 0..self.width {
            for y in 0..self.height {
                let (width, height) = (self.width, self.height);
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                // Clear the movements from the previous cycle.
                this.delta.movement_attempts.clear();
                this.delta.mate_attempts.clear();
                this.solution.coefficients = if let Some(ref decision) = this.decision {
                    decision.coefficients
                } else {
                    // Set the diffusion coefficients to the normal values.
                    [0.5, 1.0]
                };

                // Only add movements here if no cell is present.
                if this.cell.is_none() {
                    // Add any neighbor movements to the movement_attempts vector.
                    for (n, &facing) in neighbors.iter().zip(&[Direction::DownLeft,
                                                               Direction::DownRight,
                                                               Direction::Right,
                                                               Direction::UpRight,
                                                               Direction::UpLeft,
                                                               Direction::Left]) {
                        match n.decision {
                            Some(Decision { choice: Choice::Move(direction), .. }) => {
                                // It attempted to move into this hex cell.
                                if facing == direction {
                                    this.delta
                                        .movement_attempts
                                        .push(in_direction(x, y, width, height, facing.flip()));

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.movement_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            Some(Decision { choice: Choice::Divide { mate, spawn }, .. }) => {
                                // It attempted to spawn into this hex cell.
                                if facing == spawn {
                                    let source = in_direction(x, y, width, height, facing.flip());;
                                    this.delta
                                        .mate_attempts
                                        .push(Mate {
                                            mate: in_direction(source.0,
                                                               source.1,
                                                               width,
                                                               height,
                                                               mate),
                                            source: source,
                                        });

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.mate_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        // Perform the deltas.
        for x in 0..self.width {
            for y in 0..self.height {
                // Handle movement.
                if self.hex(x, y).delta.movement_attempts.len() == 1 {
                    let from_coord = self.hex(x, y).delta.movement_attempts[0];
                    self.hex_mut(x, y).cell = self.hex_mut(from_coord.0, from_coord.1).cell.take();
                    // Handle mating.
                } else if self.hex(x, y).delta.mate_attempts.len() == 1 {
                    let mate = self.hex(x, y).delta.mate_attempts[0].clone();
                    self.hex_mut(x, y).cell = if mate.mate == (x, y) {
                        Some(self.hex(mate.source.0, mate.source.1)
                            .cell
                            .as_ref()
                            .unwrap()
                            .divide(rng))
                    } else {
                        if self.hex(mate.mate.0, mate.mate.1).cell.is_some() {
                            Some(self.hex(mate.source.0, mate.source.1)
                                .cell
                                .as_ref()
                                .unwrap()
                                .mate(&self.hex(mate.mate.0, mate.mate.1)
                                          .cell
                                          .as_ref()
                                          .unwrap(),
                                      rng))
                        } else {
                            None
                        }
                    };
                }

                // Clear the decisions.
                self.hex_mut(x, y).decision = None;
            }
        }
    }

    fn cycle_fluids(&mut self) {
        // Then update diffusion.
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);

                for n in &neighbors {
                    this.solution.diffuse_from(&n.solution);
                }
            }
        }

        // Finish the cycle.
        for hex in &mut self.tiles {
            hex.solution.end_cycle();
        }
    }

    fn cycle_death(&mut self) {
        // Finish the cycle.
        for hex in &mut self.tiles {
            if hex.cell.is_some() {
                if hex.solution.fluids[0] <= CONSUMPTION {
                    if hex.cell.as_ref().unwrap().inhale != 0 {
                        hex.cell.as_mut().unwrap().inhale -= 1;
                    } else {
                        hex.cell = None;
                    }
                } else {
                    hex.solution.fluids[0] -= CONSUMPTION;
                    if hex.solution.fluids[0] < SURVIVAL_THRESHOLD {
                        if hex.cell.as_ref().unwrap().inhale != 0 {
                            hex.cell.as_mut().unwrap().inhale -= 1;
                        } else {
                            hex.cell = None;
                        }
                    } else {
                        if hex.cell.as_ref().unwrap().inhale < INHALE_CAP {
                            hex.cell.as_mut().unwrap().inhale += 1;
                        }
                    }
                }
            }
        }
    }
}

fn randomizing_vec(width: usize, height: usize, rng: &mut Isaac64Rng) -> Vec<Hex> {
    let seeds = [rng.gen(), rng.gen()];
    let noise = Brownian2::new(perlin2, 4).wavelength(32.0);
    (0..height)
        .cartesian_product((0..width))
        .map(|(x, y)| {
            Hex {
                solution: Solution::new([noise.apply(&seeds[0], &[x as f64, y as f64]), 1.0],
                                        [0.5, 1.0]),
                cell: None,
                decision: None,
                delta: Delta {
                    movement_attempts: Vec::with_capacity(6),
                    mate_attempts: Vec::with_capacity(6),
                },
            }
        })
        .collect_vec()
}

fn in_direction(x: usize,
                y: usize,
                width: usize,
                height: usize,
                direction: Direction)
                -> (usize, usize) {
    let diff = direction.delta(y % 2 == 0);
    (((width + x) as isize + diff.0) as usize % width,
     ((height + y) as isize + diff.1) as usize % height)
}
<|endoftext|>


<commit_before>use std::io::{Cursor, Write};

use byteorder::{LittleEndian, WriteBytesExt};

use error::{Error, Result};

pub const KIND_BINARY: u32 = 1;
pub const KIND_LOCATOR: u32 = 2;
pub const KIND_TEXT: u32 = 0;

const DENIED_KEYS: [&'static str; 4] = ["ID3", "TAG", "OggS", "MP+"];

/// Represents an [APE Item Value][1]
/// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_Item_Value
#[derive(Debug)]
pub enum ItemValue {
    /// Binary data. Unrecommended to use.
    Binary(Vec<u8>),
    /// Locator is an UTF-8 string contains a link to external stored information.
    Locator(String),
    /// UTF-8 string contains any Text
    Text(String),
}

/// Represents an [APE Tag Item][1].
/// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_Tag_Item
#[derive(Debug)]
pub struct Item {
    /// Item key for accessing special meta-information in an audio file.
    ///
    /// APE tag item keys can have a length of 2 (including) up to 255 (including) characters
    /// in the range from 0x20 (Space) until 0x7E (Tilde).
    ///
    /// Typical keys should have a length of 2 ... 16 characters using the following characters:
    /// Space (0x20), Slash (0x2F), Digits (0x30...0x39), Letters (0x41...0x5A, 0x61...0x7A).
    ///
    /// Not allowed are the following keys: ID3, TAG, OggS and MP+.
    ///
    /// Read the [specification][1] for more information.
    /// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_key
    pub key: String,
    /// Represents an [APE Item Value][1]
    /// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_Item_Value
    pub value: ItemValue,
}

impl Item {
    fn new<S: Into<String>>(key: S, value: ItemValue) -> Result<Item> {
        let key = key.into();
        let len = key.len();
        if len < 2 || len > 255 {
            return Err(Error::InvalidItemKeyLen);
        }
        if DENIED_KEYS.iter().any(|&dk| dk == key) {
            return Err(Error::ItemKeyDenied);
        }
        if !key.chars().all(|c| c.is_ascii()) {
            return Err(Error::InvalidItemKeyValue);
        }
        Ok(Item {
            key: key,
            value: value,
        })
    }

    /// Creates an item with Binary value.
    pub fn from_binary<S: Into<String>>(key: S, value: Vec<u8>) -> Result<Item> {
        Self::new(key, ItemValue::Binary(value))
    }

    /// Creates an item with Locator value.
    pub fn from_locator<S: Into<String>>(key: S, value: S) -> Result<Item> {
        Self::new(key, ItemValue::Locator(value.into()))
    }

    /// Creates an item with Text value.
    pub fn from_text<S: Into<String>>(key: S, value: S) -> Result<Item> {
        Self::new(key, ItemValue::Text(value.into()))
    }

    /// Sets a new Binary value.
    pub fn set_binary(&mut self, value: Vec<u8>) {
        self.value = ItemValue::Binary(value);
    }

    /// Sets a new Locator value.
    pub fn set_locator<S: Into<String>>(&mut self, value: S) {
        self.value = ItemValue::Locator(value.into());
    }

    /// Sets a new Text value.
    pub fn set_text<S: Into<String>>(&mut self, value: S) {
        self.value = ItemValue::Text(value.into());
    }

    /// Creates a representation of the item suitable for writing to a file.
    pub fn to_vec(&self) -> Result<Vec<u8>> {
        let mut cursor = Cursor::new(Vec::<u8>::new());
        let size: u32;
        let flags: u32;
        let value: &[u8];
        match self.value {
            ItemValue::Binary(ref val) => {
                size = val.len() as u32;
                flags = KIND_BINARY << 1;
                value = val;
            }
            ItemValue::Locator(ref val) => {
                size = val.len() as u32;
                flags = KIND_LOCATOR << 1;
                value = val.as_ref();
            }
            ItemValue::Text(ref val) => {
                size = val.len() as u32;
                flags = KIND_TEXT << 1;
                value = val.as_ref();
            }
        };
        cursor.write_u32::<LittleEndian>(size)?;
        cursor.write_u32::<LittleEndian>(flags)?;
        cursor.write_all(self.key.as_ref())?;
        cursor.write_u8(0)?;
        cursor.write_all(value)?;
        Ok(cursor.into_inner())
    }
}

#[cfg(test)]
mod test {
    extern crate byteorder;
    use self::byteorder::{LittleEndian, ReadBytesExt};
    use super::{Item, ItemValue, DENIED_KEYS, KIND_BINARY, KIND_LOCATOR, KIND_TEXT};
    use std::io::{Cursor, Read};

    #[test]
    #[should_panic(expected = "Item keys can have a length of 2 up to 255 characters")]
    fn new_failed_with_bad_key_len() {
        Item::from_text("k", "val").unwrap();
    }

    #[test]
    fn new_failed_with_denied_key() {
        let msg = "Not allowed are the following keys: ID3, TAG, OggS and MP+";
        for key in DENIED_KEYS.iter() {
            match Item::from_text(key.to_string(), "val".to_string()) {
                Err(err) => {
                    assert_eq!(msg, format!("{}", err));
                }
                Ok(_) => {
                    panic!("Unexpected item");
                }
            };
        }
    }

    #[test]
    #[should_panic(expected = "Item key contains non-ascii characters")]
    fn new_failed_with_bad_key_val() {
        Item::from_text("Недопустимые символы", "val").unwrap();
    }

    #[test]
    fn binary() {
        let vec: Vec<u8> = vec![1];
        let mut item = Item::from_binary("key", vec).unwrap();
        assert_eq!("key", item.key);
        assert_eq!(
            1,
            match item.value {
                ItemValue::Binary(ref val) => val,
                _ => panic!("Invalid value"),
            }[0]
        );
        let vec: Vec<u8> = vec![0];
        item.set_binary(vec);
        assert_eq!(
            0,
            match item.value {
                ItemValue::Binary(ref val) => val,
                _ => panic!("Invalid value"),
            }[0]
        );
    }

    #[test]
    fn locator() {
        let locator = "http://hostname.com";
        let mut item = Item::from_locator("key", locator).unwrap();
        assert_eq!("key", item.key);
        assert_eq!(
            locator,
            match item.value {
                ItemValue::Locator(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
        let locator = "http://another-hostname.com";
        item.set_locator(locator);
        assert_eq!(
            locator,
            match item.value {
                ItemValue::Locator(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
    }

    #[test]
    fn text() {
        let text = "text";
        let mut item = Item::from_text("key", text).unwrap();
        assert_eq!("key", item.key);
        assert_eq!(
            text,
            match item.value {
                ItemValue::Text(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
        let text = "another-text";
        item.set_text(text);
        assert_eq!(
            text,
            match item.value {
                ItemValue::Text(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
    }

    #[test]
    fn to_vec() {
        let mut data = Cursor::new(
            Item::from_binary("cover", vec![1, 2, 3])
                .unwrap()
                .to_vec()
                .unwrap(),
        );
        let item_size = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(3, item_size);
        let item_flags = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(KIND_BINARY, (item_flags & 6) >> 1);
        let mut item_key = Vec::<u8>::new();
        let mut k = data.read_u8().unwrap();
        while k != 0 {
            item_key.push(k);
            k = data.read_u8().unwrap();
        }
        assert_eq!(
            "cover",
            item_key.iter().map(|&c| c as char).collect::<String>()
        );
        let mut item_value = Vec::<u8>::with_capacity(item_size as usize);
        data.take(item_size as u64)
            .read_to_end(&mut item_value)
            .unwrap();
        assert_eq!(vec![1, 2, 3], item_value);

        let mut data = Cursor::new(
            Item::from_text("artist", "Artist")
                .unwrap()
                .to_vec()
                .unwrap(),
        );
        let item_size = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(6, item_size);
        let item_flags = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(KIND_TEXT, (item_flags & 6) >> 1);

        let mut data = Cursor::new(
            Item::from_locator("url", "http://test.com")
                .unwrap()
                .to_vec()
                .unwrap(),
        );
        let item_size = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(15, item_size);
        let item_flags = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(KIND_LOCATOR, (item_flags & 6) >> 1);
    }
}
<commit_msg>Small improvements in item module<commit_after>use std::io::{Cursor, Write};

use byteorder::{LittleEndian, WriteBytesExt};

use error::{Error, Result};

pub const KIND_BINARY: u32 = 1;
pub const KIND_LOCATOR: u32 = 2;
pub const KIND_TEXT: u32 = 0;

const DENIED_KEYS: [&'static str; 4] = ["ID3", "TAG", "OggS", "MP+"];

/// Represents an [APE Item Value][1]
/// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_Item_Value
#[derive(Debug)]
pub enum ItemValue {
    /// Binary data. Unrecommended to use.
    Binary(Vec<u8>),
    /// Locator is an UTF-8 string contains a link to external stored information.
    Locator(String),
    /// UTF-8 string contains any Text
    Text(String),
}

/// Represents an [APE Tag Item][1].
/// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_Tag_Item
#[derive(Debug)]
pub struct Item {
    /// Item key for accessing special meta-information in an audio file.
    ///
    /// APE tag item keys can have a length of 2 (including) up to 255 (including) characters
    /// in the range from 0x20 (Space) until 0x7E (Tilde).
    ///
    /// Typical keys should have a length of 2 ... 16 characters using the following characters:
    /// Space (0x20), Slash (0x2F), Digits (0x30...0x39), Letters (0x41...0x5A, 0x61...0x7A).
    ///
    /// Not allowed are the following keys: ID3, TAG, OggS and MP+.
    ///
    /// Read the [specification][1] for more information.
    /// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_key
    pub key: String,
    /// Represents an [APE Item Value][1]
    /// [1]: http://wiki.hydrogenaud.io/index.php?title=APE_Item_Value
    pub value: ItemValue,
}

impl Item {
    fn new<S: Into<String>>(key: S, value: ItemValue) -> Result<Item> {
        let key = key.into();
        let len = key.len();
        if len < 2 || len > 255 {
            return Err(Error::InvalidItemKeyLen);
        }
        if DENIED_KEYS.contains(&key.as_str()) {
            return Err(Error::ItemKeyDenied);
        }
        if key.chars().any(|c| !c.is_ascii()) {
            return Err(Error::InvalidItemKeyValue);
        }
        Ok(Item {
            key: key,
            value: value,
        })
    }

    /// Creates an item with Binary value.
    pub fn from_binary<S: Into<String>>(key: S, value: Vec<u8>) -> Result<Item> {
        Self::new(key, ItemValue::Binary(value))
    }

    /// Creates an item with Locator value.
    pub fn from_locator<S: Into<String>>(key: S, value: S) -> Result<Item> {
        Self::new(key, ItemValue::Locator(value.into()))
    }

    /// Creates an item with Text value.
    pub fn from_text<S: Into<String>>(key: S, value: S) -> Result<Item> {
        Self::new(key, ItemValue::Text(value.into()))
    }

    /// Sets a new Binary value.
    pub fn set_binary(&mut self, value: Vec<u8>) {
        self.value = ItemValue::Binary(value);
    }

    /// Sets a new Locator value.
    pub fn set_locator<S: Into<String>>(&mut self, value: S) {
        self.value = ItemValue::Locator(value.into());
    }

    /// Sets a new Text value.
    pub fn set_text<S: Into<String>>(&mut self, value: S) {
        self.value = ItemValue::Text(value.into());
    }

    /// Creates a representation of the item suitable for writing to a file.
    pub fn to_vec(&self) -> Result<Vec<u8>> {
        let mut cursor = Cursor::new(Vec::<u8>::new());
        let size: u32;
        let flags: u32;
        let value: &[u8];
        match self.value {
            ItemValue::Binary(ref val) => {
                size = val.len() as u32;
                flags = KIND_BINARY << 1;
                value = val;
            }
            ItemValue::Locator(ref val) => {
                size = val.len() as u32;
                flags = KIND_LOCATOR << 1;
                value = val.as_ref();
            }
            ItemValue::Text(ref val) => {
                size = val.len() as u32;
                flags = KIND_TEXT << 1;
                value = val.as_ref();
            }
        };
        cursor.write_u32::<LittleEndian>(size)?;
        cursor.write_u32::<LittleEndian>(flags)?;
        cursor.write_all(self.key.as_ref())?;
        cursor.write_u8(0)?;
        cursor.write_all(value)?;
        Ok(cursor.into_inner())
    }
}

#[cfg(test)]
mod test {
    extern crate byteorder;
    use self::byteorder::{LittleEndian, ReadBytesExt};
    use super::{Item, ItemValue, DENIED_KEYS, KIND_BINARY, KIND_LOCATOR, KIND_TEXT};
    use std::io::{Cursor, Read};

    #[test]
    #[should_panic(expected = "Item keys can have a length of 2 up to 255 characters")]
    fn new_failed_with_bad_key_len() {
        Item::from_text("k", "val").unwrap();
    }

    #[test]
    fn new_failed_with_denied_key() {
        let msg = "Not allowed are the following keys: ID3, TAG, OggS and MP+";
        for key in DENIED_KEYS.iter() {
            match Item::from_text(key.to_string(), "val".to_string()) {
                Err(err) => {
                    assert_eq!(msg, format!("{}", err));
                }
                Ok(_) => {
                    panic!("Unexpected item");
                }
            };
        }
    }

    #[test]
    #[should_panic(expected = "Item key contains non-ascii characters")]
    fn new_failed_with_bad_key_val() {
        Item::from_text("Недопустимые символы", "val").unwrap();
    }

    #[test]
    fn binary() {
        let vec: Vec<u8> = vec![1];
        let mut item = Item::from_binary("key", vec).unwrap();
        assert_eq!("key", item.key);
        assert_eq!(
            1,
            match item.value {
                ItemValue::Binary(ref val) => val,
                _ => panic!("Invalid value"),
            }[0]
        );
        let vec: Vec<u8> = vec![0];
        item.set_binary(vec);
        assert_eq!(
            0,
            match item.value {
                ItemValue::Binary(ref val) => val,
                _ => panic!("Invalid value"),
            }[0]
        );
    }

    #[test]
    fn locator() {
        let locator = "http://hostname.com";
        let mut item = Item::from_locator("key", locator).unwrap();
        assert_eq!("key", item.key);
        assert_eq!(
            locator,
            match item.value {
                ItemValue::Locator(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
        let locator = "http://another-hostname.com";
        item.set_locator(locator);
        assert_eq!(
            locator,
            match item.value {
                ItemValue::Locator(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
    }

    #[test]
    fn text() {
        let text = "text";
        let mut item = Item::from_text("key", text).unwrap();
        assert_eq!("key", item.key);
        assert_eq!(
            text,
            match item.value {
                ItemValue::Text(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
        let text = "another-text";
        item.set_text(text);
        assert_eq!(
            text,
            match item.value {
                ItemValue::Text(ref val) => val,
                _ => panic!("Invalid value"),
            }
        );
    }

    #[test]
    fn to_vec() {
        let mut data = Cursor::new(
            Item::from_binary("cover", vec![1, 2, 3])
                .unwrap()
                .to_vec()
                .unwrap(),
        );
        let item_size = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(3, item_size);
        let item_flags = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(KIND_BINARY, (item_flags & 6) >> 1);
        let mut item_key = Vec::<u8>::new();
        let mut k = data.read_u8().unwrap();
        while k != 0 {
            item_key.push(k);
            k = data.read_u8().unwrap();
        }
        assert_eq!(
            "cover",
            item_key.iter().map(|&c| c as char).collect::<String>()
        );
        let mut item_value = Vec::<u8>::with_capacity(item_size as usize);
        data.take(item_size as u64)
            .read_to_end(&mut item_value)
            .unwrap();
        assert_eq!(vec![1, 2, 3], item_value);

        let mut data = Cursor::new(
            Item::from_text("artist", "Artist")
                .unwrap()
                .to_vec()
                .unwrap(),
        );
        let item_size = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(6, item_size);
        let item_flags = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(KIND_TEXT, (item_flags & 6) >> 1);

        let mut data = Cursor::new(
            Item::from_locator("url", "http://test.com")
                .unwrap()
                .to_vec()
                .unwrap(),
        );
        let item_size = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(15, item_size);
        let item_flags = data.read_u32::<LittleEndian>().unwrap();
        assert_eq!(KIND_LOCATOR, (item_flags & 6) >> 1);
    }
}
<|endoftext|>