<commit_before>// Copyright 2013-2014 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::test::TestFn;
use std::char;
use std::fmt::Write;

use idna::Errors;

pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
    // https://www.unicode.org/Public/idna/13.0.0/IdnaTestV2.txt
    for (i, line) in include_str!("IdnaTestV2.txt").lines().enumerate() {
        if line.is_empty() || line.starts_with('#') {
            continue;
        }

        // Remove comments
        let line = match line.find('#') {
            Some(index) => &line[0..index],
            None => line,
        };

        let mut pieces = line.split(';').map(|x| x.trim()).collect::<Vec<&str>>();
        let source = unescape(pieces.remove(0));

        // ToUnicode
        let mut to_unicode = unescape(pieces.remove(0));
        if to_unicode.is_empty() {
            to_unicode = source.clone();
        }
        let to_unicode_status = status(pieces.remove(0));

        // ToAsciiN
        let to_ascii_n = pieces.remove(0);
        let to_ascii_n = if to_ascii_n.is_empty() {
            to_unicode.clone()
        } else {
            to_ascii_n.to_owned()
        };
        let to_ascii_n_status = pieces.remove(0);
        let to_ascii_n_status = if to_ascii_n_status.is_empty() {
            to_unicode_status.clone()
        } else {
            status(to_ascii_n_status)
        };

        // ToAsciiT
        let to_ascii_t = pieces.remove(0);
        let to_ascii_t = if to_ascii_t.is_empty() {
            to_ascii_n.clone()
        } else {
            to_ascii_t.to_owned()
        };
        let to_ascii_t_status = pieces.remove(0);
        let to_ascii_t_status = if to_ascii_t_status.is_empty() {
            to_ascii_n_status.clone()
        } else {
            status(to_ascii_t_status)
        };

        let test_name = format!("UTS #46 line {}", i + 1);
        add_test(
            test_name,
            TestFn::DynTestFn(Box::new(move || {
                let config = idna::Config::default()
                    .use_std3_ascii_rules(true)
                    .verify_dns_length(true)
                    .check_hyphens(true);

                // http://unicode.org/reports/tr46/#Deviations
                // applications that perform IDNA2008 lookup are not required to check
                // for these contexts, so we skip all tests annotated with C*

                // Everybody ignores V2
                // https://github.com/servo/rust-url/pull/240
                // https://github.com/whatwg/url/issues/53#issuecomment-181528158
                // http://www.unicode.org/review/pri317/

                // "The special error codes X3 and X4_2 are now returned where a toASCII error code
                // was formerly being generated in toUnicode due to an empty label."
                // This is not implemented yet, so we skip toUnicode X4_2 tests for now, too.

                let (to_unicode_value, to_unicode_result) =
                    config.transitional_processing(false).to_unicode(&source);
                let to_unicode_result = to_unicode_result.map(|()| to_unicode_value);
                check(
                    &source,
                    (&to_unicode, &to_unicode_status),
                    to_unicode_result,
                    |e| e.starts_with('C') || e == "V2" || e == "X4_2",
                );

                let to_ascii_n_result = config.transitional_processing(false).to_ascii(&source);
                check(
                    &source,
                    (&to_ascii_n, &to_ascii_n_status),
                    to_ascii_n_result,
                    |e| e.starts_with('C') || e == "V2",
                );

                let to_ascii_t_result = config.transitional_processing(true).to_ascii(&source);
                check(
                    &source,
                    (&to_ascii_t, &to_ascii_t_status),
                    to_ascii_t_result,
                    |e| e.starts_with('C') || e == "V2",
                );
            })),
        )
    }
}

#[allow(clippy::redundant_clone)]
fn check<F>(source: &str, expected: (&str, &[&str]), actual: Result<String, Errors>, ignore: F)
where
    F: Fn(&str) -> bool,
{
    if !expected.1.is_empty() {
        if !expected.1.iter().copied().any(ignore) {
            let res = actual.ok();
            assert_eq!(
                res.clone(),
                None,
                "Expected error {:?}. result: {} | source: {}",
                expected.1,
                res.unwrap(),
                source,
            );
        }
    } else {
        assert!(
            actual.is_ok(),
            "Couldn't parse {} | error: {:?}",
            source,
            actual.err().unwrap(),
        );
        assert_eq!(actual.unwrap(), expected.0, "source: {}", source);
    }
}

fn unescape(input: &str) -> String {
    let mut output = String::new();
    let mut chars = input.chars();
    loop {
        match chars.next() {
            None => return output,
            Some(c) => {
                if c == '\\' {
                    match chars.next().unwrap() {
                        '\\' => output.push('\\'),
                        'u' => {
                            let c1 = chars.next().unwrap().to_digit(16).unwrap();
                            let c2 = chars.next().unwrap().to_digit(16).unwrap();
                            let c3 = chars.next().unwrap().to_digit(16).unwrap();
                            let c4 = chars.next().unwrap().to_digit(16).unwrap();
                            match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4) {
                                Some(c) => output.push(c),
                                None => {
                                    write!(&mut output,"\\u{:X}{:X}{:X}{:X}", c1, c2, c3, c4).expect("Could not write to output");
                                }
                            };
                        }
                        _ => panic!("Invalid test data input"),
                    }
                } else {
                    output.push(c);
                }
            }
        }
    }
}

fn status(status: &str) -> Vec<&str> {
    if status.is_empty() || status == "[]" {
        return Vec::new();
    }

    let mut result = status.split(", ").collect::<Vec<_>>();
    assert!(result[0].starts_with('['));
    result[0] = &result[0][1..];

    let idx = result.len() - 1;
    let last = &mut result[idx];
    assert!(last.ends_with(']'));
    *last = &last[..last.len() - 1];

    result
}
<commit_msg>Fix fmt<commit_after>// Copyright 2013-2014 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::test::TestFn;
use std::char;
use std::fmt::Write;

use idna::Errors;

pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
    // https://www.unicode.org/Public/idna/13.0.0/IdnaTestV2.txt
    for (i, line) in include_str!("IdnaTestV2.txt").lines().enumerate() {
        if line.is_empty() || line.starts_with('#') {
            continue;
        }

        // Remove comments
        let line = match line.find('#') {
            Some(index) => &line[0..index],
            None => line,
        };

        let mut pieces = line.split(';').map(|x| x.trim()).collect::<Vec<&str>>();
        let source = unescape(pieces.remove(0));

        // ToUnicode
        let mut to_unicode = unescape(pieces.remove(0));
        if to_unicode.is_empty() {
            to_unicode = source.clone();
        }
        let to_unicode_status = status(pieces.remove(0));

        // ToAsciiN
        let to_ascii_n = pieces.remove(0);
        let to_ascii_n = if to_ascii_n.is_empty() {
            to_unicode.clone()
        } else {
            to_ascii_n.to_owned()
        };
        let to_ascii_n_status = pieces.remove(0);
        let to_ascii_n_status = if to_ascii_n_status.is_empty() {
            to_unicode_status.clone()
        } else {
            status(to_ascii_n_status)
        };

        // ToAsciiT
        let to_ascii_t = pieces.remove(0);
        let to_ascii_t = if to_ascii_t.is_empty() {
            to_ascii_n.clone()
        } else {
            to_ascii_t.to_owned()
        };
        let to_ascii_t_status = pieces.remove(0);
        let to_ascii_t_status = if to_ascii_t_status.is_empty() {
            to_ascii_n_status.clone()
        } else {
            status(to_ascii_t_status)
        };

        let test_name = format!("UTS #46 line {}", i + 1);
        add_test(
            test_name,
            TestFn::DynTestFn(Box::new(move || {
                let config = idna::Config::default()
                    .use_std3_ascii_rules(true)
                    .verify_dns_length(true)
                    .check_hyphens(true);

                // http://unicode.org/reports/tr46/#Deviations
                // applications that perform IDNA2008 lookup are not required to check
                // for these contexts, so we skip all tests annotated with C*

                // Everybody ignores V2
                // https://github.com/servo/rust-url/pull/240
                // https://github.com/whatwg/url/issues/53#issuecomment-181528158
                // http://www.unicode.org/review/pri317/

                // "The special error codes X3 and X4_2 are now returned where a toASCII error code
                // was formerly being generated in toUnicode due to an empty label."
                // This is not implemented yet, so we skip toUnicode X4_2 tests for now, too.

                let (to_unicode_value, to_unicode_result) =
                    config.transitional_processing(false).to_unicode(&source);
                let to_unicode_result = to_unicode_result.map(|()| to_unicode_value);
                check(
                    &source,
                    (&to_unicode, &to_unicode_status),
                    to_unicode_result,
                    |e| e.starts_with('C') || e == "V2" || e == "X4_2",
                );

                let to_ascii_n_result = config.transitional_processing(false).to_ascii(&source);
                check(
                    &source,
                    (&to_ascii_n, &to_ascii_n_status),
                    to_ascii_n_result,
                    |e| e.starts_with('C') || e == "V2",
                );

                let to_ascii_t_result = config.transitional_processing(true).to_ascii(&source);
                check(
                    &source,
                    (&to_ascii_t, &to_ascii_t_status),
                    to_ascii_t_result,
                    |e| e.starts_with('C') || e == "V2",
                );
            })),
        )
    }
}

#[allow(clippy::redundant_clone)]
fn check<F>(source: &str, expected: (&str, &[&str]), actual: Result<String, Errors>, ignore: F)
where
    F: Fn(&str) -> bool,
{
    if !expected.1.is_empty() {
        if !expected.1.iter().copied().any(ignore) {
            let res = actual.ok();
            assert_eq!(
                res.clone(),
                None,
                "Expected error {:?}. result: {} | source: {}",
                expected.1,
                res.unwrap(),
                source,
            );
        }
    } else {
        assert!(
            actual.is_ok(),
            "Couldn't parse {} | error: {:?}",
            source,
            actual.err().unwrap(),
        );
        assert_eq!(actual.unwrap(), expected.0, "source: {}", source);
    }
}

fn unescape(input: &str) -> String {
    let mut output = String::new();
    let mut chars = input.chars();
    loop {
        match chars.next() {
            None => return output,
            Some(c) => {
                if c == '\\' {
                    match chars.next().unwrap() {
                        '\\' => output.push('\\'),
                        'u' => {
                            let c1 = chars.next().unwrap().to_digit(16).unwrap();
                            let c2 = chars.next().unwrap().to_digit(16).unwrap();
                            let c3 = chars.next().unwrap().to_digit(16).unwrap();
                            let c4 = chars.next().unwrap().to_digit(16).unwrap();
                            match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4) {
                                Some(c) => output.push(c),
                                None => {
                                    write!(&mut output, "\\u{:X}{:X}{:X}{:X}", c1, c2, c3, c4)
                                        .expect("Could not write to output");
                                }
                            };
                        }
                        _ => panic!("Invalid test data input"),
                    }
                } else {
                    output.push(c);
                }
            }
        }
    }
}

fn status(status: &str) -> Vec<&str> {
    if status.is_empty() || status == "[]" {
        return Vec::new();
    }

    let mut result = status.split(", ").collect::<Vec<_>>();
    assert!(result[0].starts_with('['));
    result[0] = &result[0][1..];

    let idx = result.len() - 1;
    let last = &mut result[idx];
    assert!(last.ends_with(']'));
    *last = &last[..last.len() - 1];

    result
}
<|endoftext|>


<commit_before>// This file provides functions to create a libxml2 xmlParserCtxtPtr, configured
// to read from a gio::InputStream, and to maintain its loading data in an XmlState.

use gio;
use gio::prelude::*;
use gio_sys;
use glib_sys;
use std::mem;
use std::ptr;
use std::slice;
use std::str;

use glib::translate::*;

use error::{set_gerror, RsvgError};
use property_bag::PropertyBag;
use util::utf8_cstr;
use xml::XmlState;
use xml2::*;

extern "C" {
    fn rsvg_sax_error_cb(data: *mut libc::c_void);
}

fn get_xml2_sax_handler() -> xmlSAXHandler {
    let mut h: xmlSAXHandler = unsafe { mem::zeroed() };

    h.getEntity = Some(sax_get_entity_cb);
    h.entityDecl = Some(sax_entity_decl_cb);
    h.unparsedEntityDecl = Some(sax_unparsed_entity_decl_cb);
    h.getParameterEntity = Some(sax_get_parameter_entity_cb);
    h.characters = Some(sax_characters_cb);
    h.cdataBlock = Some(sax_characters_cb);
    h.startElement = Some(sax_start_element_cb);
    h.endElement = Some(sax_end_element_cb);
    h.processingInstruction = Some(sax_processing_instruction_cb);

    // This one is defined in the C code, because the prototype has varargs
    // and we can't handle those from Rust :(
    h.error = rsvg_sax_error_cb as *mut _;

    h
}

fn free_xml_parser_and_doc(parser: xmlParserCtxtPtr) {
    unsafe {
        if !parser.is_null() {
            let rparser = &mut *parser;

            if !rparser.myDoc.is_null() {
                xmlFreeDoc(rparser.myDoc);
                rparser.myDoc = ptr::null_mut();
            }

            xmlFreeParserCtxt(parser);
        }
    }
}

unsafe extern "C" fn sax_get_entity_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
) -> xmlEntityPtr {
    let xml = &*(ctx as *mut XmlState);

    assert!(!name.is_null());
    let name = utf8_cstr(name);

    xml.entity_lookup(name).unwrap_or(ptr::null_mut())
}

unsafe extern "C" fn sax_entity_decl_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
    type_: libc::c_int,
    _public_id: *const libc::c_char,
    _system_id: *const libc::c_char,
    content: *const libc::c_char,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!name.is_null());

    if type_ != XML_INTERNAL_GENERAL_ENTITY {
        // We don't allow loading external entities; we don't support
        // defining parameter entities in the DTD, and libxml2 should
        // handle internal predefined entities by itself (e.g. "&").
        return;
    }

    let entity = xmlNewEntity(
        ptr::null_mut(),
        name,
        type_,
        ptr::null(),
        ptr::null(),
        content,
    );
    assert!(!entity.is_null());

    let name = utf8_cstr(name);
    xml.entity_insert(name, entity);
}

unsafe extern "C" fn sax_unparsed_entity_decl_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
    public_id: *const libc::c_char,
    system_id: *const libc::c_char,
    _notation_name: *const libc::c_char,
) {
    sax_entity_decl_cb(
        ctx,
        name,
        XML_INTERNAL_GENERAL_ENTITY,
        public_id,
        system_id,
        ptr::null(),
    );
}

unsafe extern "C" fn sax_start_element_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
    atts: *const *const libc::c_char,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!name.is_null());
    let name = utf8_cstr(name);

    let pbag = PropertyBag::new_from_key_value_pairs(atts);

    xml.start_element(name, &pbag);
}

unsafe extern "C" fn sax_end_element_cb(ctx: *mut libc::c_void, name: *const libc::c_char) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!name.is_null());
    let name = utf8_cstr(name);

    xml.end_element(name);
}

unsafe extern "C" fn sax_characters_cb(
    ctx: *mut libc::c_void,
    unterminated_text: *const libc::c_char,
    len: libc::c_int,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!unterminated_text.is_null());
    assert!(len >= 0);

    // libxml2 already validated the incoming string as UTF-8.  Note that
    // it is *not* nul-terminated; this is why we create a byte slice first.
    let bytes = std::slice::from_raw_parts(unterminated_text as *const u8, len as usize);
    let utf8 = str::from_utf8_unchecked(bytes);

    xml.characters(utf8);
}

unsafe extern "C" fn sax_processing_instruction_cb(
    ctx: *mut libc::c_void,
    target: *const libc::c_char,
    data: *const libc::c_char,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!target.is_null());
    let target = utf8_cstr(target);

    assert!(!data.is_null());
    let data = utf8_cstr(data);

    xml.processing_instruction(target, data);
}

unsafe extern "C" fn sax_get_parameter_entity_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
) -> xmlEntityPtr {
    sax_get_entity_cb(ctx, name)
}

fn set_xml_parse_options(parser: xmlParserCtxtPtr, unlimited_size: bool) {
    let mut options: libc::c_int = XML_PARSE_NONET | XML_PARSE_BIG_LINES;

    if unlimited_size {
        options |= XML_PARSE_HUGE;
    }

    unsafe {
        xmlCtxtUseOptions(parser, options);

        // If false, external entities work, but internal ones don't. if
        // true, internal entities work, but external ones don't. favor
        // internal entities, in order to not cause a regression
        (*parser).replaceEntities = 1;
    }
}

struct StreamCtx {
    stream: gio::InputStream,
    cancellable: Option<gio::Cancellable>,
    error: *mut *mut glib_sys::GError,
}

// read() callback from xmlCreateIOParserCtxt()
unsafe extern "C" fn stream_ctx_read(
    context: *mut libc::c_void,
    buffer: *mut libc::c_char,
    len: libc::c_int,
) -> libc::c_int {
    let ctx = &mut *(context as *mut StreamCtx);

    // has the error been set already?
    if !(*ctx.error).is_null() {
        return -1;
    }

    let buf: &mut [u8] = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);

    match ctx.stream.read(buf, ctx.cancellable.as_ref()) {
        Ok(size) => size as libc::c_int,

        Err(e) => {
            let e: *const glib_sys::GError = e.to_glib_full();
            *ctx.error = e as *mut _;
            -1
        }
    }
}

// close() callback from xmlCreateIOParserCtxt()
unsafe extern "C" fn stream_ctx_close(context: *mut libc::c_void) -> libc::c_int {
    let ctx = &mut *(context as *mut StreamCtx);

    let ret = match ctx.stream.close(ctx.cancellable.as_ref()) {
        Ok(()) => 0,

        Err(e) => {
            // don't overwrite a previous error
            if (*ctx.error).is_null() {
                let e: *const glib_sys::GError = e.to_glib_full();
                *ctx.error = e as *mut _;
            }

            -1
        }
    };

    Box::from_raw(ctx);

    ret
}

fn create_xml_stream_parser(
    xml: &mut XmlState,
    unlimited_size: bool,
    stream: gio::InputStream,
    cancellable: Option<gio::Cancellable>,
    error: *mut *mut glib_sys::GError,
) -> Result<xmlParserCtxtPtr, glib::Error> {
    let ctx = Box::new(StreamCtx {
        stream,
        cancellable,
        error,
    });

    let mut sax_handler = get_xml2_sax_handler();

    unsafe {
        let parser = xmlCreateIOParserCtxt(
            &mut sax_handler,
            xml as *mut _ as *mut _,
            Some(stream_ctx_read),
            Some(stream_ctx_close),
            Box::into_raw(ctx) as *mut _,
            XML_CHAR_ENCODING_NONE,
        );

        if parser.is_null() {
            // on error, xmlCreateIOParserCtxt() frees our context via the
            // stream_ctx_close function
            Err(glib::Error::new(RsvgError, "Error creating XML parser"))
        } else {
            set_xml_parse_options(parser, unlimited_size);
            Ok(parser)
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_create_xml_push_parser(
    xml: *mut XmlState,
    unlimited_size: glib_sys::gboolean,
    base_uri: *const libc::c_char,
    error: *mut *mut glib_sys::GError,
) -> xmlParserCtxtPtr {
    let mut sax_handler = get_xml2_sax_handler();

    let parser = xmlCreatePushParserCtxt(&mut sax_handler, xml as *mut _, ptr::null(), 0, base_uri);

    if parser.is_null() {
        set_gerror(error, 0, "Error creating XML parser");
    } else {
        set_xml_parse_options(parser, from_glib(unlimited_size));
    }

    parser
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_set_error_from_xml(
    error: *mut *mut glib_sys::GError,
    ctxt: xmlParserCtxtPtr,
) {
    let xerr = xmlCtxtGetLastError(ctxt as *mut _);

    if !xerr.is_null() {
        let xerr = &*xerr;

        let file = if xerr.file.is_null() {
            "data".to_string()
        } else {
            from_glib_none(xerr.file)
        };

        let message = if xerr.message.is_null() {
            "-".to_string()
        } else {
            from_glib_none(xerr.message)
        };

        let msg = format!(
            "Error domain {} code {} on line {} column {} of {}: {}",
            xerr.domain, xerr.code, xerr.line, xerr.int2, file, message
        );

        set_gerror(error, 0, &msg);
    } else {
        set_gerror(error, 0, "Error parsing XML data");
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_xml_state_parse_from_stream(
    xml: *mut XmlState,
    unlimited_size: glib_sys::gboolean,
    stream: *mut gio_sys::GInputStream,
    cancellable: *mut gio_sys::GCancellable,
    error: *mut *mut glib_sys::GError,
) -> glib_sys::gboolean {
    assert!(!xml.is_null());
    let xml = &mut *xml;

    let unlimited_size = from_glib(unlimited_size);

    let stream = from_glib_none(stream);
    let cancellable = from_glib_none(cancellable);

    let mut err: *mut glib_sys::GError = ptr::null_mut();

    match create_xml_stream_parser(xml, unlimited_size, stream, cancellable, &mut err) {
        Ok(parser) => {
            let xml_parse_success = xmlParseDocument(parser) == 0;

            let svg_parse_success = err.is_null();

            if !svg_parse_success {
                if !error.is_null() {
                    *error = err;
                }
            } else if !xml_parse_success {
                rsvg_set_error_from_xml(error, parser);
            }

            free_xml_parser_and_doc(parser);

            (svg_parse_success && xml_parse_success).to_glib()
        }

        Err(e) => {
            if !error.is_null() {
                *error = e.to_glib_full() as *mut _;
            }

            false.to_glib()
        }
    }
}
<commit_msg>xml2_load.rs: Clarify which errors are GIO errors vs. XML parsing errors<commit_after>// This file provides functions to create a libxml2 xmlParserCtxtPtr, configured
// to read from a gio::InputStream, and to maintain its loading data in an XmlState.

use gio;
use gio::prelude::*;
use gio_sys;
use glib_sys;
use std::mem;
use std::ptr;
use std::slice;
use std::str;

use glib::translate::*;

use error::{set_gerror, RsvgError};
use property_bag::PropertyBag;
use util::utf8_cstr;
use xml::XmlState;
use xml2::*;

extern "C" {
    fn rsvg_sax_error_cb(data: *mut libc::c_void);
}

fn get_xml2_sax_handler() -> xmlSAXHandler {
    let mut h: xmlSAXHandler = unsafe { mem::zeroed() };

    h.getEntity = Some(sax_get_entity_cb);
    h.entityDecl = Some(sax_entity_decl_cb);
    h.unparsedEntityDecl = Some(sax_unparsed_entity_decl_cb);
    h.getParameterEntity = Some(sax_get_parameter_entity_cb);
    h.characters = Some(sax_characters_cb);
    h.cdataBlock = Some(sax_characters_cb);
    h.startElement = Some(sax_start_element_cb);
    h.endElement = Some(sax_end_element_cb);
    h.processingInstruction = Some(sax_processing_instruction_cb);

    // This one is defined in the C code, because the prototype has varargs
    // and we can't handle those from Rust :(
    h.error = rsvg_sax_error_cb as *mut _;

    h
}

fn free_xml_parser_and_doc(parser: xmlParserCtxtPtr) {
    unsafe {
        if !parser.is_null() {
            let rparser = &mut *parser;

            if !rparser.myDoc.is_null() {
                xmlFreeDoc(rparser.myDoc);
                rparser.myDoc = ptr::null_mut();
            }

            xmlFreeParserCtxt(parser);
        }
    }
}

unsafe extern "C" fn sax_get_entity_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
) -> xmlEntityPtr {
    let xml = &*(ctx as *mut XmlState);

    assert!(!name.is_null());
    let name = utf8_cstr(name);

    xml.entity_lookup(name).unwrap_or(ptr::null_mut())
}

unsafe extern "C" fn sax_entity_decl_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
    type_: libc::c_int,
    _public_id: *const libc::c_char,
    _system_id: *const libc::c_char,
    content: *const libc::c_char,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!name.is_null());

    if type_ != XML_INTERNAL_GENERAL_ENTITY {
        // We don't allow loading external entities; we don't support
        // defining parameter entities in the DTD, and libxml2 should
        // handle internal predefined entities by itself (e.g. "&").
        return;
    }

    let entity = xmlNewEntity(
        ptr::null_mut(),
        name,
        type_,
        ptr::null(),
        ptr::null(),
        content,
    );
    assert!(!entity.is_null());

    let name = utf8_cstr(name);
    xml.entity_insert(name, entity);
}

unsafe extern "C" fn sax_unparsed_entity_decl_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
    public_id: *const libc::c_char,
    system_id: *const libc::c_char,
    _notation_name: *const libc::c_char,
) {
    sax_entity_decl_cb(
        ctx,
        name,
        XML_INTERNAL_GENERAL_ENTITY,
        public_id,
        system_id,
        ptr::null(),
    );
}

unsafe extern "C" fn sax_start_element_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
    atts: *const *const libc::c_char,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!name.is_null());
    let name = utf8_cstr(name);

    let pbag = PropertyBag::new_from_key_value_pairs(atts);

    xml.start_element(name, &pbag);
}

unsafe extern "C" fn sax_end_element_cb(ctx: *mut libc::c_void, name: *const libc::c_char) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!name.is_null());
    let name = utf8_cstr(name);

    xml.end_element(name);
}

unsafe extern "C" fn sax_characters_cb(
    ctx: *mut libc::c_void,
    unterminated_text: *const libc::c_char,
    len: libc::c_int,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!unterminated_text.is_null());
    assert!(len >= 0);

    // libxml2 already validated the incoming string as UTF-8.  Note that
    // it is *not* nul-terminated; this is why we create a byte slice first.
    let bytes = std::slice::from_raw_parts(unterminated_text as *const u8, len as usize);
    let utf8 = str::from_utf8_unchecked(bytes);

    xml.characters(utf8);
}

unsafe extern "C" fn sax_processing_instruction_cb(
    ctx: *mut libc::c_void,
    target: *const libc::c_char,
    data: *const libc::c_char,
) {
    let xml = &mut *(ctx as *mut XmlState);

    assert!(!target.is_null());
    let target = utf8_cstr(target);

    assert!(!data.is_null());
    let data = utf8_cstr(data);

    xml.processing_instruction(target, data);
}

unsafe extern "C" fn sax_get_parameter_entity_cb(
    ctx: *mut libc::c_void,
    name: *const libc::c_char,
) -> xmlEntityPtr {
    sax_get_entity_cb(ctx, name)
}

fn set_xml_parse_options(parser: xmlParserCtxtPtr, unlimited_size: bool) {
    let mut options: libc::c_int = XML_PARSE_NONET | XML_PARSE_BIG_LINES;

    if unlimited_size {
        options |= XML_PARSE_HUGE;
    }

    unsafe {
        xmlCtxtUseOptions(parser, options);

        // If false, external entities work, but internal ones don't. if
        // true, internal entities work, but external ones don't. favor
        // internal entities, in order to not cause a regression
        (*parser).replaceEntities = 1;
    }
}

// Struct used as closure data for xmlCreateIOParserCtxt().  In conjunction
// with stream_ctx_read() and stream_ctx_close(), this struct provides the
// I/O callbacks and their context for libxml2.
//
// We call I/O methods on the stream, and as soon as we get an error
// we store it in the gio_error field.  Libxml2 just allows us to
// return -1 from the I/O callbacks in that case; it doesn't actually
// see the error code.
//
// The gio_error field comes from the place that constructs the
// StreamCtx.  That place is later responsible for seeing if the error
// is set; if it is, it means that there was an I/O error.  Otherwise,
// there were no I/O errors but the caller must then ask libxml2 for
// XML parsing errors.
struct StreamCtx {
    stream: gio::InputStream,
    cancellable: Option<gio::Cancellable>,
    gio_error: *mut *mut glib_sys::GError,
}

// read() callback from xmlCreateIOParserCtxt()
unsafe extern "C" fn stream_ctx_read(
    context: *mut libc::c_void,
    buffer: *mut libc::c_char,
    len: libc::c_int,
) -> libc::c_int {
    let ctx = &mut *(context as *mut StreamCtx);

    // has the error been set already?
    if !(*ctx.gio_error).is_null() {
        return -1;
    }

    let buf: &mut [u8] = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);

    match ctx.stream.read(buf, ctx.cancellable.as_ref()) {
        Ok(size) => size as libc::c_int,

        Err(e) => {
            let e: *const glib_sys::GError = e.to_glib_full();
            *ctx.gio_error = e as *mut _;
            -1
        }
    }
}

// close() callback from xmlCreateIOParserCtxt()
unsafe extern "C" fn stream_ctx_close(context: *mut libc::c_void) -> libc::c_int {
    let ctx = &mut *(context as *mut StreamCtx);

    let ret = match ctx.stream.close(ctx.cancellable.as_ref()) {
        Ok(()) => 0,

        Err(e) => {
            // don't overwrite a previous error
            if (*ctx.gio_error).is_null() {
                let e: *const glib_sys::GError = e.to_glib_full();
                *ctx.gio_error = e as *mut _;
            }

            -1
        }
    };

    Box::from_raw(ctx);

    ret
}

fn create_xml_stream_parser(
    xml: &mut XmlState,
    unlimited_size: bool,
    stream: gio::InputStream,
    cancellable: Option<gio::Cancellable>,
    gio_error: *mut *mut glib_sys::GError,
) -> Result<xmlParserCtxtPtr, glib::Error> {
    let ctx = Box::new(StreamCtx {
        stream,
        cancellable,
        gio_error,
    });

    let mut sax_handler = get_xml2_sax_handler();

    unsafe {
        let parser = xmlCreateIOParserCtxt(
            &mut sax_handler,
            xml as *mut _ as *mut _,
            Some(stream_ctx_read),
            Some(stream_ctx_close),
            Box::into_raw(ctx) as *mut _,
            XML_CHAR_ENCODING_NONE,
        );

        if parser.is_null() {
            // on error, xmlCreateIOParserCtxt() frees our context via the
            // stream_ctx_close function
            Err(glib::Error::new(RsvgError, "Error creating XML parser"))
        } else {
            set_xml_parse_options(parser, unlimited_size);
            Ok(parser)
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_create_xml_push_parser(
    xml: *mut XmlState,
    unlimited_size: glib_sys::gboolean,
    base_uri: *const libc::c_char,
    error: *mut *mut glib_sys::GError,
) -> xmlParserCtxtPtr {
    let mut sax_handler = get_xml2_sax_handler();

    let parser = xmlCreatePushParserCtxt(&mut sax_handler, xml as *mut _, ptr::null(), 0, base_uri);

    if parser.is_null() {
        set_gerror(error, 0, "Error creating XML parser");
    } else {
        set_xml_parse_options(parser, from_glib(unlimited_size));
    }

    parser
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_set_error_from_xml(
    error: *mut *mut glib_sys::GError,
    ctxt: xmlParserCtxtPtr,
) {
    let xerr = xmlCtxtGetLastError(ctxt as *mut _);

    if !xerr.is_null() {
        let xerr = &*xerr;

        let file = if xerr.file.is_null() {
            "data".to_string()
        } else {
            from_glib_none(xerr.file)
        };

        let message = if xerr.message.is_null() {
            "-".to_string()
        } else {
            from_glib_none(xerr.message)
        };

        let msg = format!(
            "Error domain {} code {} on line {} column {} of {}: {}",
            xerr.domain, xerr.code, xerr.line, xerr.int2, file, message
        );

        set_gerror(error, 0, &msg);
    } else {
        set_gerror(error, 0, "Error parsing XML data");
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_xml_state_parse_from_stream(
    xml: *mut XmlState,
    unlimited_size: glib_sys::gboolean,
    stream: *mut gio_sys::GInputStream,
    cancellable: *mut gio_sys::GCancellable,
    error: *mut *mut glib_sys::GError,
) -> glib_sys::gboolean {
    assert!(!xml.is_null());
    let xml = &mut *xml;

    let unlimited_size = from_glib(unlimited_size);

    let stream = from_glib_none(stream);
    let cancellable = from_glib_none(cancellable);

    let mut gio_err: *mut glib_sys::GError = ptr::null_mut();

    match create_xml_stream_parser(xml, unlimited_size, stream, cancellable, &mut gio_err) {
        Ok(parser) => {
            let xml_parse_success = xmlParseDocument(parser) == 0;

            let io_success = gio_err.is_null();

            if !io_success {
                if !error.is_null() {
                    *error = gio_err;
                }
            } else if !xml_parse_success {
                rsvg_set_error_from_xml(error, parser);
            }

            free_xml_parser_and_doc(parser);

            (io_success && xml_parse_success).to_glib()
        }

        Err(e) => {
            if !error.is_null() {
                *error = e.to_glib_full() as *mut _;
            }

            false.to_glib()
        }
    }
}
<|endoftext|>


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

#[derive(Debug)]
pub struct CircularBuffer<T> {
    buffer: VecDeque<T>,
    size: usize,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    EmptyBuffer,
    FullBuffer,
}

impl<T> CircularBuffer<T> {
    pub fn new(size: usize) -> Self {
        CircularBuffer {
            buffer: VecDeque::new(),
            size: size,
        }
    }

    pub fn read(&mut self) -> Result<T, Error> {
        if self.size == 0 {
            return Err(Error::FullBuffer);
        }

        self.buffer.pop_front().ok_or(Error::EmptyBuffer)
    }

    pub fn write(&mut self, v: T) -> Result<usize, Error> {
        if self.size == 0 || self.buffer.len() == self.size {
            return Err(Error::FullBuffer);
        }

        self.buffer.push_back(v);

        Ok(self.buffer.len())
    }

    pub fn overwrite(&mut self, v: T) {
        if self.size == 0 {
            return;
        }

        if self.buffer.len() == self.size {
            self.buffer.pop_front().unwrap();
        }

        self.buffer.push_back(v);
    }

    pub fn clear(&mut self) {
        self.buffer.clear()
    }
}
<commit_msg>Add special error code for the case size is 0<commit_after>use std::collections::VecDeque;

#[derive(Debug)]
pub struct CircularBuffer<T> {
    buffer: VecDeque<T>,
    size: usize,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidSize,
    EmptyBuffer,
    FullBuffer,
}

impl<T> CircularBuffer<T> {
    pub fn new(size: usize) -> Self {
        CircularBuffer {
            buffer: VecDeque::new(),
            size: size,
        }
    }

    pub fn read(&mut self) -> Result<T, Error> {
        if self.size == 0 {
            return Err(Error::InvalidSize);
        }

        self.buffer.pop_front().ok_or(Error::EmptyBuffer)
    }

    pub fn write(&mut self, v: T) -> Result<usize, Error> {
        if self.size == 0 {
            return Err(Error::InvalidSize);
        }

        if self.buffer.len() == self.size {
            return Err(Error::FullBuffer);
        }

        self.buffer.push_back(v);

        Ok(self.buffer.len())
    }

    pub fn overwrite(&mut self, v: T) {
        if self.size == 0 {
            return;
        }

        if self.buffer.len() == self.size {
            self.buffer.pop_front().unwrap();
        }

        self.buffer.push_back(v);
    }

    pub fn clear(&mut self) {
        self.buffer.clear()
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// 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
//
//   http://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.

//! An implementation of base index with most common features.
use std::borrow::Cow;
use std::marker::PhantomData;

use super::{StorageKey, StorageValue, Snapshot, Fork, Iter};

/// Basic struct for all indices that implements common features.
///
/// This structure is not intended for direct use, rather it is the basis for building other types
/// of indices.
///
/// `BaseIndex` requires that the keys implement the [`StorageKey`] trait and the values implement
/// [`StorageValue`] trait. However, this structure is not bound to specific types and allows the
/// use of *any* types as keys or values.
/// [`StorageKey`]: ../trait.StorageKey.html
/// [`StorageValue`]: ../trait.StorageValue.html
#[derive(Debug)]
pub struct BaseIndex<T> {
    name: String,
    prefix: Option<Vec<u8>>,
    view: T,
}

/// An iterator over the entries of a `BaseIndex`.
///
/// This struct is created by the [`iter`] or
/// [`iter_from`] methods on [`BaseIndex`]. See its documentation for more.
///
/// [`iter`]: struct.BaseIndex.html#method.iter
/// [`iter_from`]: struct.BaseIndex.html#method.iter_from
/// [`BaseIndex`]: struct.BaseIndex.html
pub struct BaseIndexIter<'a, K, V> {
    base_iter: Iter<'a>,
    base_prefix_len: usize,
    prefix: Vec<u8>,
    ended: bool,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
}

impl<T> BaseIndex<T> {
    /// Creates a new index representation based on the name and storage view.
    ///
    /// Storage view can be specified as [`&Snapshot`] or [`&mut Fork`]. In the first case only
    /// immutable methods are available. In the second case both immutable and mutable methods are
    /// available.
    /// [`&Snapshot`]: ../trait.Snapshot.html
    /// [`&mut Fork`]: ../struct.Fork.html
    pub fn new<S: AsRef<str>>(name: S, view: T) -> Self {
        if is_valid_name(&name) {
            BaseIndex {
                name: name.as_ref().to_string(),
                prefix: None,
                view,
            }
        } else {
            panic!("Wrong characters using in name. Use: a-zA-Z0-9 and _");
        }
    }

    /// Creates a new index representation based on the name, common prefix of its keys
    /// and storage view.
    ///
    /// Storage view can be specified as [`&Snapshot`] or [`&mut Fork`]. In the first case only
    /// immutable methods are available. In the second case both immutable and mutable methods are
    /// available.
    /// [`&Snapshot`]: ../trait.Snapshot.html
    /// [`&mut Fork`]: ../struct.Fork.html
    pub fn with_prefix<S: AsRef<str>>(name: S, prefix: Vec<u8>, view: T) -> Self {
        if is_valid_name(&name) {
            BaseIndex {
                name: name.as_ref().to_string(),
                prefix: Some(prefix),
                view,
            }
        } else {
            panic!("Wrong characters using in name. Use: a-zA-Z0-9 and _");
        }
    }

    fn prefixed_key<K: StorageKey>(&self, key: &K) -> Vec<u8> {
        match self.prefix {
            Some(ref prefix) => {
                let mut v = vec![0; prefix.len() + key.size()];
                v[..prefix.len()].copy_from_slice(prefix);
                key.write(&mut v[prefix.len()..]);
                v
            }
            None => {
                let mut v = vec![0; key.size()];
                key.write(&mut v);
                v
            }
        }
    }
}

impl<T> BaseIndex<T>
where
    T: AsRef<Snapshot>,
{
    /// Returns a value of *any* type corresponding to the key of *any* type.
    pub fn get<K, V>(&self, key: &K) -> Option<V>
    where
        K: StorageKey,
        V: StorageValue,
    {
        self.view
            .as_ref()
            .get(&self.name, &self.prefixed_key(key))
            .map(|v| StorageValue::from_bytes(Cow::Owned(v)))
    }

    /// Returns `true` if the index contains a value of *any* type for the specified key of
    /// *any* type.
    pub fn contains<K>(&self, key: &K) -> bool
    where
        K: StorageKey,
    {
        self.view.as_ref().contains(
            &self.name,
            &self.prefixed_key(key),
        )
    }

    /// Returns an iterator over the entries of the index in ascending order. The iterator element
    /// type is *any* key-value pair. An argument `subprefix` allows to specify a subset of keys
    /// for iteration.
    pub fn iter<P, K, V>(&self, subprefix: &P) -> BaseIndexIter<K, V>
    where
        P: StorageKey,
        K: StorageKey,
        V: StorageValue,
    {
        let iter_prefix = self.prefixed_key(subprefix);
        BaseIndexIter {
            base_iter: self.view.as_ref().iter(&self.name, &iter_prefix),
            base_prefix_len: self.prefix.as_ref().map_or(0, |p| p.len()),
            prefix: iter_prefix,
            ended: false,
            _k: PhantomData,
            _v: PhantomData,
        }
    }

    /// Returns an iterator over the entries of the index in ascending order starting from the
    /// specified key. The iterator element type is *any* key-value pair. An argument `subprefix`
    /// allows to specify a subset of iteration.
    pub fn iter_from<P, F, K, V>(&self, subprefix: &P, from: &F) -> BaseIndexIter<K, V>
    where
        P: StorageKey,
        F: StorageKey,
        K: StorageKey,
        V: StorageValue,
    {
        let iter_prefix = self.prefixed_key(subprefix);
        let iter_from = self.prefixed_key(from);
        BaseIndexIter {
            base_iter: self.view.as_ref().iter(&self.name, &iter_from),
            base_prefix_len: self.prefix.as_ref().map_or(0, |p| p.len()),
            prefix: iter_prefix,
            ended: false,
            _k: PhantomData,
            _v: PhantomData,
        }
    }
}

impl<'a> BaseIndex<&'a mut Fork> {
    /// Inserts the key-value pair into the index. Both key and value may be of *any* types.
    pub fn put<K, V>(&mut self, key: &K, value: V)
    where
        K: StorageKey,
        V: StorageValue,
    {
        let key = self.prefixed_key(key);
        self.view.put(&self.name, key, value.into_bytes());
    }

    /// Removes the key of *any* type from the index.
    pub fn remove<K>(&mut self, key: &K)
    where
        K: StorageKey,
    {
        let key = self.prefixed_key(key);
        self.view.remove(&self.name, key);
    }

    /// Clears the index, removing entries with keys that starts with a prefix or all entries
    /// if `prefix` is `None`.
    ///
    /// # Notes
    ///
    /// Currently this method is not optimized to delete large set of data. During the execution of
    /// this method the amount of allocated memory is linearly dependent on the number of elements
    /// in the index.
    pub fn clear(&mut self) {
        self.view.remove_by_prefix(&self.name, self.prefix.as_ref());
    }
}

impl<'a, K, V> Iterator for BaseIndexIter<'a, K, V>
where
    K: StorageKey,
    V: StorageValue,
{
    type Item = (K, V);

    fn next(&mut self) -> Option<Self::Item> {
        if self.ended {
            return None;
        }
        if let Some((k, v)) = self.base_iter.next() {
            if k.starts_with(&self.prefix) {
                return Some((
                    K::read(&k[self.base_prefix_len..]),
                    V::from_bytes(Cow::Borrowed(v)),
                ));
            }
        }
        self.ended = true;
        None
    }
}

impl<'a, K, V> ::std::fmt::Debug for BaseIndexIter<'a, K, V> {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "BaseIndexIter(..)")
    }
}

/// A function that validates an index name. Allowable characters in name: ASCII characters, digits
/// and underscores.
fn is_valid_name<S: AsRef<str>>(name: S) -> bool {
    name.as_ref().as_bytes().iter().all(|c| match *c {
        48...57 | 65...90 | 97...122 | 95 | 46 => true,
        _ => false,
    })
}

#[test]
fn test_index_name_validator() {
    assert!(is_valid_name("index_name"));
    assert!(is_valid_name("_index_name"));
    assert!(is_valid_name("AinDex_name_"));
    assert!(is_valid_name("core.index_name1Z"));
    assert!(is_valid_name("configuration.indeX_1namE"));
    assert!(is_valid_name("1index_Namez"));

    assert!(!is_valid_name("index-name"));
    assert!(!is_valid_name("_index-name"));
    assert!(!is_valid_name("индекс_name_"));
    assert!(!is_valid_name("core.index_имя3"));
    assert!(!is_valid_name("indeX_1namE-"));
    assert!(!is_valid_name("1in!dex_Namez"));
}
<commit_msg>Move panic after index name validation into the separate function<commit_after>// Copyright 2017 The Exonum Team
//
// 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
//
//   http://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.

//! An implementation of base index with most common features.
use std::borrow::Cow;
use std::marker::PhantomData;

use super::{StorageKey, StorageValue, Snapshot, Fork, Iter};

/// Basic struct for all indices that implements common features.
///
/// This structure is not intended for direct use, rather it is the basis for building other types
/// of indices.
///
/// `BaseIndex` requires that the keys implement the [`StorageKey`] trait and the values implement
/// [`StorageValue`] trait. However, this structure is not bound to specific types and allows the
/// use of *any* types as keys or values.
/// [`StorageKey`]: ../trait.StorageKey.html
/// [`StorageValue`]: ../trait.StorageValue.html
#[derive(Debug)]
pub struct BaseIndex<T> {
    name: String,
    prefix: Option<Vec<u8>>,
    view: T,
}

/// An iterator over the entries of a `BaseIndex`.
///
/// This struct is created by the [`iter`] or
/// [`iter_from`] methods on [`BaseIndex`]. See its documentation for more.
///
/// [`iter`]: struct.BaseIndex.html#method.iter
/// [`iter_from`]: struct.BaseIndex.html#method.iter_from
/// [`BaseIndex`]: struct.BaseIndex.html
pub struct BaseIndexIter<'a, K, V> {
    base_iter: Iter<'a>,
    base_prefix_len: usize,
    prefix: Vec<u8>,
    ended: bool,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
}

impl<T> BaseIndex<T> {
    /// Creates a new index representation based on the name and storage view.
    ///
    /// Storage view can be specified as [`&Snapshot`] or [`&mut Fork`]. In the first case only
    /// immutable methods are available. In the second case both immutable and mutable methods are
    /// available.
    /// [`&Snapshot`]: ../trait.Snapshot.html
    /// [`&mut Fork`]: ../struct.Fork.html
    pub fn new<S: AsRef<str>>(name: S, view: T) -> Self {
        assert_valid_name(&name);

        BaseIndex {
            name: name.as_ref().to_string(),
            prefix: None,
            view,
        }
    }

    /// Creates a new index representation based on the name, common prefix of its keys
    /// and storage view.
    ///
    /// Storage view can be specified as [`&Snapshot`] or [`&mut Fork`]. In the first case only
    /// immutable methods are available. In the second case both immutable and mutable methods are
    /// available.
    /// [`&Snapshot`]: ../trait.Snapshot.html
    /// [`&mut Fork`]: ../struct.Fork.html
    pub fn with_prefix<S: AsRef<str>>(name: S, prefix: Vec<u8>, view: T) -> Self {
        assert_valid_name(&name);

        BaseIndex {
            name: name.as_ref().to_string(),
            prefix: Some(prefix),
            view,
        }
    }

    fn prefixed_key<K: StorageKey>(&self, key: &K) -> Vec<u8> {
        match self.prefix {
            Some(ref prefix) => {
                let mut v = vec![0; prefix.len() + key.size()];
                v[..prefix.len()].copy_from_slice(prefix);
                key.write(&mut v[prefix.len()..]);
                v
            }
            None => {
                let mut v = vec![0; key.size()];
                key.write(&mut v);
                v
            }
        }
    }
}

impl<T> BaseIndex<T>
where
    T: AsRef<Snapshot>,
{
    /// Returns a value of *any* type corresponding to the key of *any* type.
    pub fn get<K, V>(&self, key: &K) -> Option<V>
    where
        K: StorageKey,
        V: StorageValue,
    {
        self.view
            .as_ref()
            .get(&self.name, &self.prefixed_key(key))
            .map(|v| StorageValue::from_bytes(Cow::Owned(v)))
    }

    /// Returns `true` if the index contains a value of *any* type for the specified key of
    /// *any* type.
    pub fn contains<K>(&self, key: &K) -> bool
    where
        K: StorageKey,
    {
        self.view.as_ref().contains(
            &self.name,
            &self.prefixed_key(key),
        )
    }

    /// Returns an iterator over the entries of the index in ascending order. The iterator element
    /// type is *any* key-value pair. An argument `subprefix` allows to specify a subset of keys
    /// for iteration.
    pub fn iter<P, K, V>(&self, subprefix: &P) -> BaseIndexIter<K, V>
    where
        P: StorageKey,
        K: StorageKey,
        V: StorageValue,
    {
        let iter_prefix = self.prefixed_key(subprefix);
        BaseIndexIter {
            base_iter: self.view.as_ref().iter(&self.name, &iter_prefix),
            base_prefix_len: self.prefix.as_ref().map_or(0, |p| p.len()),
            prefix: iter_prefix,
            ended: false,
            _k: PhantomData,
            _v: PhantomData,
        }
    }

    /// Returns an iterator over the entries of the index in ascending order starting from the
    /// specified key. The iterator element type is *any* key-value pair. An argument `subprefix`
    /// allows to specify a subset of iteration.
    pub fn iter_from<P, F, K, V>(&self, subprefix: &P, from: &F) -> BaseIndexIter<K, V>
    where
        P: StorageKey,
        F: StorageKey,
        K: StorageKey,
        V: StorageValue,
    {
        let iter_prefix = self.prefixed_key(subprefix);
        let iter_from = self.prefixed_key(from);
        BaseIndexIter {
            base_iter: self.view.as_ref().iter(&self.name, &iter_from),
            base_prefix_len: self.prefix.as_ref().map_or(0, |p| p.len()),
            prefix: iter_prefix,
            ended: false,
            _k: PhantomData,
            _v: PhantomData,
        }
    }
}

impl<'a> BaseIndex<&'a mut Fork> {
    /// Inserts the key-value pair into the index. Both key and value may be of *any* types.
    pub fn put<K, V>(&mut self, key: &K, value: V)
    where
        K: StorageKey,
        V: StorageValue,
    {
        let key = self.prefixed_key(key);
        self.view.put(&self.name, key, value.into_bytes());
    }

    /// Removes the key of *any* type from the index.
    pub fn remove<K>(&mut self, key: &K)
    where
        K: StorageKey,
    {
        let key = self.prefixed_key(key);
        self.view.remove(&self.name, key);
    }

    /// Clears the index, removing entries with keys that starts with a prefix or all entries
    /// if `prefix` is `None`.
    ///
    /// # Notes
    ///
    /// Currently this method is not optimized to delete large set of data. During the execution of
    /// this method the amount of allocated memory is linearly dependent on the number of elements
    /// in the index.
    pub fn clear(&mut self) {
        self.view.remove_by_prefix(&self.name, self.prefix.as_ref());
    }
}

impl<'a, K, V> Iterator for BaseIndexIter<'a, K, V>
where
    K: StorageKey,
    V: StorageValue,
{
    type Item = (K, V);

    fn next(&mut self) -> Option<Self::Item> {
        if self.ended {
            return None;
        }
        if let Some((k, v)) = self.base_iter.next() {
            if k.starts_with(&self.prefix) {
                return Some((
                    K::read(&k[self.base_prefix_len..]),
                    V::from_bytes(Cow::Borrowed(v)),
                ));
            }
        }
        self.ended = true;
        None
    }
}

impl<'a, K, V> ::std::fmt::Debug for BaseIndexIter<'a, K, V> {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "BaseIndexIter(..)")
    }
}

/// A function that validates an index name. Allowable characters in name: ASCII characters, digits
/// and underscores.
fn is_valid_name<S: AsRef<str>>(name: S) -> bool {
    name.as_ref().as_bytes().iter().all(|c| match *c {
        48...57 | 65...90 | 97...122 | 95 | 46 => true,
        _ => false,
    })
}

/// Calls the `is_valid_name` function with the given name and panics if it returns `false`.
fn assert_valid_name<S: AsRef<str>>(name: S) {
    if !is_valid_name(name) {
        panic!("Wrong characters using in name. Use: a-zA-Z0-9 and _");
    }
}

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

    #[test]
    fn test_index_name_validator() {
        assert!(is_valid_name("index_name"));
        assert!(is_valid_name("_index_name"));
        assert!(is_valid_name("AinDex_name_"));
        assert!(is_valid_name("core.index_name1Z"));
        assert!(is_valid_name("configuration.indeX_1namE"));
        assert!(is_valid_name("1index_Namez"));

        assert!(!is_valid_name("index-name"));
        assert!(!is_valid_name("_index-name"));
        assert!(!is_valid_name("индекс_name_"));
        assert!(!is_valid_name("core.index_имя3"));
        assert!(!is_valid_name("indeX_1namE-"));
        assert!(!is_valid_name("1in!dex_Namez"));
    }

    #[test]
    fn check_valid_name() {
        assert_valid_name("valid_name");
    }

    #[test]
    #[should_panic(expected = "Wrong characters using in name. Use: a-zA-Z0-9 and _")]
    fn check_invalid_name() {
        assert_valid_name("invalid-name");
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// 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
//
//     http://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.

//! A syntax highlighting plugin based on syntect.

extern crate syntect;
#[macro_use]
extern crate xi_plugin_lib;

use xi_plugin_lib::caching_plugin::{self, PluginCtx, SpansBuilder};

use syntect::parsing::{ParseState, ScopeStack, SyntaxSet};
use syntect::highlighting::{Color, FontStyle, Highlighter, HighlightIterator, HighlightState,
    Style, ThemeSet};

fn color_to_rgba(color: Color) -> u32 {
    ((color.a as u32) << 24) | ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.r as u32)
}

fn font_style_to_u8(fs: FontStyle) -> u8 {
    fs.bits()
}

fn add_style_span(builder: &mut SpansBuilder, style: Style, start: usize, end: usize) {
    builder.add_style_span(start, end,
        color_to_rgba(style.foreground), font_style_to_u8(style.font_style));
}

struct Sets {
    ss: SyntaxSet,
    ts: ThemeSet,
}

struct PluginState<'a> {
    sets: &'a Sets,
    line_num: usize,
    offset: usize,
    parse_state: Option<ParseState>,
    highlighter: Option<Highlighter<'a>>,
    hstate: Option<HighlightState>,
    spans_start: usize,
    builder: Option<SpansBuilder>,
}

impl<'a> PluginState<'a> {
    pub fn new(sets: &'a Sets) -> Self {
        PluginState {
            sets: sets,
            line_num: 0,
            offset: 0,
            parse_state: None,
            highlighter: None,
            hstate: None,
            spans_start: 0,
            builder: None,
        }
    }

    // Return true if there's more to do.
    fn highlight_one_line(&mut self, ctx: &mut PluginCtx) -> bool {
        let line = ctx.get_line(self.line_num);
        if let Err(err) = line {
            print_err!("Error: {:?}", err);
            return false;
        }
        let line = line.unwrap();
        if line.is_none() {
            return false;
        }
        let line = line.unwrap();
        let ops = self.parse_state.as_mut().unwrap().parse_line(&line);
        if self.builder.is_none() {
            self.spans_start = self.offset;
            self.builder = Some(SpansBuilder::new());
        }
        let iter = HighlightIterator::new(self.hstate.as_mut().unwrap(), &ops, &line,
            self.highlighter.as_ref().unwrap());
        let mut ix = 0;
        for (style, str_slice) in iter {
            let start = self.offset - self.spans_start + ix;
            let end = start + str_slice.len();
            add_style_span(self.builder.as_mut().unwrap(), style, start, end);
            ix += str_slice.len();
        }
        self.line_num += 1;
        self.offset += line.len();
        true
    }

    fn flush_spans(&mut self, ctx: &mut PluginCtx) {
        if let Some(builder) = self.builder.take() {
            ctx.set_fg_spans(self.spans_start, self.offset - self.spans_start, builder.build());
        }
    }

    fn do_highlighting(&mut self, mut ctx: PluginCtx) {
        let syntax = match ctx.get_path() {
            Some(ref path) => self.sets.ss.find_syntax_for_file(path).unwrap()
                .unwrap_or_else(|| self.sets.ss.find_syntax_plain_text()),
            None => self.sets.ss.find_syntax_plain_text(),
        };
        print_err!("syntect using {}", syntax.name);

        self.parse_state = Some(ParseState::new(syntax));
        let theme = &self.sets.ts.themes["InspiredGitHub"];
        self.highlighter = Some(Highlighter::new(theme));
        self.hstate = Some(HighlightState::new(self.highlighter.as_ref().unwrap(),
            ScopeStack::new()));
        self.line_num = 0;
        self.offset = 0;
        ctx.schedule_idle(0);
    }
}

const LINES_PER_RPC: usize = 50;

impl<'a> caching_plugin::Handler for PluginState<'a> {
    fn initialize(&mut self, ctx: PluginCtx, _buf_size: usize) {
        self.do_highlighting(ctx);
    }

    fn update(&mut self, ctx: PluginCtx) {
        self.do_highlighting(ctx);
    }

    fn idle(&mut self, mut ctx: PluginCtx, _token: usize) {
        print_err!("idle task at line {}", self.line_num);
        for _ in 0..LINES_PER_RPC {
            if !self.highlight_one_line(&mut ctx) {
                self.flush_spans(&mut ctx);
                return;
            }
            if ctx.request_is_pending() {
                print_err!("request pending at line {}", self.line_num);
                break;
            }
        }
        self.flush_spans(&mut ctx);
        ctx.schedule_idle(0);
    }
}

fn main() {
    let sets = Sets {
        ss: SyntaxSet::load_defaults_newlines(),
        ts: ThemeSet::load_defaults(),
    };
    let mut state = PluginState::new(&sets);

    caching_plugin::mainloop(&mut state);
}
<commit_msg>Fix syntect color, fewer log msgs<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// 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
//
//     http://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.

//! A syntax highlighting plugin based on syntect.

extern crate syntect;
#[macro_use]
extern crate xi_plugin_lib;

use xi_plugin_lib::caching_plugin::{self, PluginCtx, SpansBuilder};

use syntect::parsing::{ParseState, ScopeStack, SyntaxSet};
use syntect::highlighting::{Color, FontStyle, Highlighter, HighlightIterator, HighlightState,
    Style, ThemeSet};

fn color_to_rgba(color: Color) -> u32 {
    ((color.a as u32) << 24) | ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32)
}

fn font_style_to_u8(fs: FontStyle) -> u8 {
    fs.bits()
}

fn add_style_span(builder: &mut SpansBuilder, style: Style, start: usize, end: usize) {
    builder.add_style_span(start, end,
        color_to_rgba(style.foreground), font_style_to_u8(style.font_style));
}

struct Sets {
    ss: SyntaxSet,
    ts: ThemeSet,
}

struct PluginState<'a> {
    sets: &'a Sets,
    line_num: usize,
    offset: usize,
    parse_state: Option<ParseState>,
    highlighter: Option<Highlighter<'a>>,
    hstate: Option<HighlightState>,
    spans_start: usize,
    builder: Option<SpansBuilder>,
    syntax_name: String,
}

impl<'a> PluginState<'a> {
    pub fn new(sets: &'a Sets) -> Self {
        PluginState {
            sets: sets,
            line_num: 0,
            offset: 0,
            parse_state: None,
            highlighter: None,
            hstate: None,
            spans_start: 0,
            builder: None,
            syntax_name: String::from("None"),
        }
    }

    // Return true if there's more to do.
    fn highlight_one_line(&mut self, ctx: &mut PluginCtx) -> bool {
        let line = ctx.get_line(self.line_num);
        if let Err(err) = line {
            print_err!("Error: {:?}", err);
            return false;
        }
        let line = line.unwrap();
        if line.is_none() {
            return false;
        }
        let line = line.unwrap();
        let ops = self.parse_state.as_mut().unwrap().parse_line(&line);
        if self.builder.is_none() {
            self.spans_start = self.offset;
            self.builder = Some(SpansBuilder::new());
        }
        let iter = HighlightIterator::new(self.hstate.as_mut().unwrap(), &ops, &line,
            self.highlighter.as_ref().unwrap());
        let mut ix = 0;
        for (style, str_slice) in iter {
            let start = self.offset - self.spans_start + ix;
            let end = start + str_slice.len();
            add_style_span(self.builder.as_mut().unwrap(), style, start, end);
            ix += str_slice.len();
        }
        self.line_num += 1;
        self.offset += line.len();
        true
    }

    fn flush_spans(&mut self, ctx: &mut PluginCtx) {
        if let Some(builder) = self.builder.take() {
            ctx.set_fg_spans(self.spans_start, self.offset - self.spans_start, builder.build());
        }
    }

    fn do_highlighting(&mut self, mut ctx: PluginCtx) {
        let syntax = match ctx.get_path() {
            Some(ref path) => self.sets.ss.find_syntax_for_file(path).unwrap()
                .unwrap_or_else(|| self.sets.ss.find_syntax_plain_text()),
            None => self.sets.ss.find_syntax_plain_text(),
        };

        if syntax.name != self.syntax_name {
            self.syntax_name = syntax.name.clone();
            print_err!("syntect using {}", syntax.name);
        }

        self.parse_state = Some(ParseState::new(syntax));
        let theme = &self.sets.ts.themes["InspiredGitHub"];
        self.highlighter = Some(Highlighter::new(theme));
        self.hstate = Some(HighlightState::new(self.highlighter.as_ref().unwrap(),
            ScopeStack::new()));
        self.line_num = 0;
        self.offset = 0;
        ctx.schedule_idle(0);
    }
}

const LINES_PER_RPC: usize = 50;

impl<'a> caching_plugin::Handler for PluginState<'a> {
    fn initialize(&mut self, ctx: PluginCtx, _buf_size: usize) {
        self.do_highlighting(ctx);
    }

    fn update(&mut self, ctx: PluginCtx) {
        self.do_highlighting(ctx);
    }

    fn idle(&mut self, mut ctx: PluginCtx, _token: usize) {
        print_err!("idle task at line {}", self.line_num);
        for _ in 0..LINES_PER_RPC {
            if !self.highlight_one_line(&mut ctx) {
                self.flush_spans(&mut ctx);
                return;
            }
            if ctx.request_is_pending() {
                print_err!("request pending at line {}", self.line_num);
                break;
            }
        }
        self.flush_spans(&mut ctx);
        ctx.schedule_idle(0);
    }
}

fn main() {
    let sets = Sets {
        ss: SyntaxSet::load_defaults_newlines(),
        ts: ThemeSet::load_defaults(),
    };
    let mut state = PluginState::new(&sets);

    caching_plugin::mainloop(&mut state);
}
<|endoftext|>


<commit_before>use std::cmp::Reverse;
use std::io::Read;

struct LowPointsIter<'a> {
    lines: &'a [&'a [u8]],
    x: usize,
    y: usize,
}

impl<'a> LowPointsIter<'a> {
    pub fn new(lines: &'a [&'a [u8]]) -> Self {
        Self { lines, x: 0, y: 0 }
    }
}

impl Iterator for LowPointsIter<'_> {
    type Item = (usize, usize);

    fn next(&mut self) -> Option<Self::Item> {
        let lines = self.lines;

        for y in self.y..self.lines.len() {
            for x in self.x..self.lines[y].len() {
                if x > 0 && lines[y][x - 1] <= lines[y][x] {
                    continue;
                }

                if x + 1 < lines[y].len() && lines[y][x + 1] <= lines[y][x] {
                    continue;
                }

                if y > 0 && lines[y - 1][x] <= lines[y][x] {
                    continue;
                }

                if y + 1 < lines.len() && lines[y + 1][x] <= lines[y][x] {
                    continue;
                }

                self.x = x + 1;
                self.y = y;

                return Some((x, y));
            }
            self.x = 0;
        }

        self.y = lines.len();

        None
    }
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut buffer = Vec::new();
    input.read_to_end(&mut buffer).unwrap();

    let lines: Vec<_> = buffer
        .split(|&s| s == b'\n')
        .filter(|s| !s.is_empty())
        .collect();

    LowPointsIter::new(&lines)
        .map(|(x, y)| 1 + (lines[y][x] - b'0') as i32)
        .sum::<i32>()
        .to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut buffer = Vec::new();
    input.read_to_end(&mut buffer).unwrap();

    let mut visited_buffer = vec![false; buffer.len()];

    let lines: Vec<_> = buffer
        .split(|&s| s == b'\n')
        .filter(|s| !s.is_empty())
        .collect();

    let mut visited: Vec<_> = visited_buffer.chunks_exact_mut(lines[0].len()).collect();

    let mut todo = Vec::new();

    let mut sizes = Vec::with_capacity(4);

    for (x, y) in LowPointsIter::new(&lines) {
        todo.push((x, y));

        let mut size = 1;
        visited[y][x] = true;

        while let Some((x, y)) = todo.pop() {
            let mut add = |x: usize, y: usize| {
                if lines[y][x] != b'9' && !visited[y][x] {
                    size += 1;
                    visited[y][x] = true;
                    todo.push((x, y));
                }
            };

            if x > 0 {
                add(x - 1, y);
            }

            if x + 1 < lines[y].len() {
                add(x + 1, y);
            }

            if y > 0 {
                add(x, y - 1)
            }

            if y + 1 < lines.len() {
                add(x, y + 1);
            }
        }

        sizes.push(Reverse(size));
        sizes.sort_unstable();
        sizes.truncate(3);
    }

    sizes.into_iter().fold(1, |a, Reverse(b)| a * b).to_string()
}

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

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/09.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 15);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 1134);
    }
}
<commit_msg>Simplify implementation day 8<commit_after>use std::cmp::Reverse;
use std::io::Read;

pub fn part1(input: &mut dyn Read) -> String {
    let mut buffer = Vec::new();
    input.read_to_end(&mut buffer).unwrap();

    let lines: Vec<_> = buffer
        .split(|&s| s == b'\n')
        .filter(|s| !s.is_empty())
        .collect();

    let mut total_danger = 0;

    for y in 0..lines.len() {
        for x in 0..lines[y].len() {
            if x > 0 && lines[y][x - 1] <= lines[y][x] {
                continue;
            }

            if x + 1 < lines[y].len() && lines[y][x + 1] <= lines[y][x] {
                continue;
            }

            if y > 0 && lines[y - 1][x] <= lines[y][x] {
                continue;
            }

            if y + 1 < lines.len() && lines[y + 1][x] <= lines[y][x] {
                continue;
            }

            total_danger += 1 + (lines[y][x] - b'0') as i32;
        }
    }

    total_danger.to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut buffer = Vec::new();
    input.read_to_end(&mut buffer).unwrap();

    let mut visited_buffer = vec![false; buffer.len()];

    let lines: Vec<_> = buffer
        .split(|&s| s == b'\n')
        .filter(|s| !s.is_empty())
        .collect();

    let mut visited: Vec<_> = visited_buffer.chunks_exact_mut(lines[0].len()).collect();

    let mut todo = Vec::new();

    let mut sizes = Vec::with_capacity(4);

    for y in 0..lines.len() {
        for x in 0..lines[0].len() {
            if visited[y][x] || lines[y][x] == b'9' {
                continue;
            }

            todo.push((x, y));
            let mut size = 1;
            visited[y][x] = true;

            while let Some((x, y)) = todo.pop() {
                let mut add = |x: usize, y: usize| {
                    if lines[y][x] != b'9' && !visited[y][x] {
                        size += 1;
                        visited[y][x] = true;
                        todo.push((x, y));
                    }
                };

                if x > 0 {
                    add(x - 1, y);
                }

                if x + 1 < lines[y].len() {
                    add(x + 1, y);
                }

                if y > 0 {
                    add(x, y - 1)
                }

                if y + 1 < lines.len() {
                    add(x, y + 1);
                }
            }

            sizes.push(Reverse(size));
            sizes.sort_unstable();
            sizes.truncate(3);
        }
    }

    sizes.into_iter().fold(1, |a, Reverse(b)| a * b).to_string()
}

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

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/09.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 15);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 1134);
    }
}
<|endoftext|>


<commit_before>use num::Integer;
use euclid::Point2D;
use hex::Dir6;

pub trait FovValue: Eq + Clone {
    /// Construct a new FovValue based on previous one and a new point in the fov.
    fn advance(&self, offset: Point2D<i32>) -> Option<Self>;

    /// Optional method for showing acute corners of fake isometric rooms in fov.
    fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
        let _ = offset;
        false
    }
}

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that maps cells into user-specified values and indicates when traversal should
/// stop.
pub struct HexFov<T> {
    stack: Vec<Sector<T>>,
    /// Extra values generated by special cases.
    side_channel: Vec<(Point2D<i32>, T)>,
}

impl<T: FovValue> HexFov<T> {
    pub fn new(init: T) -> HexFov<T> {
        // We could run f for (0, 0) here, but the traditional way for the FOV to work is to only
        // consider your surroundings, not the origin site itself.
        HexFov {
            stack: vec![Sector {
                            begin: PolarPoint::new(0.0, 1),
                            pt: PolarPoint::new(0.0, 1),
                            end: PolarPoint::new(6.0, 1),
                            prev_value: init.clone(),
                            group_value: None,
                        }],
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![(Point2D::new(0, 0), init)],
        }
    }
}

impl<T: FovValue> Iterator for HexFov<T> {
    type Item = (Point2D<i32>, T);
    fn next(&mut self) -> Option<(Point2D<i32>, T)> {
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        if let Some(mut current) = self.stack.pop() {
            let current_value = current.prev_value.advance(current.pt.to_v2());

            if current.pt.is_below(current.end) && current_value.is_some() {
                let pos = current.pt.to_v2();
                let current_value = current_value.unwrap();

                match current.group_value {
                    None => {
                        // Beginning of group, value isn't set.
                        current.group_value = Some(current_value.clone());
                    }
                    Some(ref g) if g.clone() != current_value => {
                        // Value changed, branch out.

                        // Add the rest of this sector with the new value.
                        self.stack.push(Sector {
                            begin: current.pt,
                            pt: current.pt,
                            end: current.end,
                            prev_value: current.prev_value,
                            group_value: Some(current_value.clone()),
                        });

                        // Branch further on the arc processed so far.
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            prev_value: g.clone(),
                            group_value: None,
                        });
                        return self.next();
                    }
                    _ => {}
                }

                // Current value and group value are assumed to be the same from here on.
                assert!(current.group_value == Some(current_value.clone()));

                // Hack for making acute corner tiles of fake-isometric rooms visible.

                // We're moving along a vertical line on the hex circle, so there are side
                // points to check.
                if let Some(side_pos) = current.pt.side_point() {

                    let next = current.pt.next();
                    // If the next cell is within the current span and the current cell is
                    // wallform,
                    if next.is_below(current.end) &&
                       current.prev_value.is_fake_isometric_wall(current.pt.to_v2()) {
                        // and if the next cell is visible,
                        if let Some(next_value) = current.prev_value.advance(next.to_v2()) {
                            // and if the current and the next cell are in the same value group,
                            // both the next cell and the third corner point cell are
                            // wallforms, and the side point would not be otherwise
                            // visible:
                            if next_value == current.prev_value &&
                               next_value.is_fake_isometric_wall(next.to_v2()) &&
                               current.prev_value.advance(side_pos).is_none() &&
                               current.prev_value.is_fake_isometric_wall(side_pos) {
                                // Add the side point to the side channel.
                                self.side_channel.push((side_pos, current.prev_value.clone()));
                            }
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some((pos, current_value));
            } else {
                // Hit the end of the sector, branch ahead.
                if let Some(value) = current.group_value {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        prev_value: value,
                        group_value: None,
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector<T> {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// The user value from previous iteration.
    prev_value: T,
    /// The user value for this group.
    group_value: Option<T>,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32,
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint {
        PolarPoint {
            pos: pos,
            radius: radius,
        }
    }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }

    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }

    pub fn to_v2(self) -> Point2D<i32> {
        if self.radius == 0 {
            return Point2D::new(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return the point outside of the circle between the two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<Point2D<i32>> {
        let next = self.next();
        let a = self.to_v2();
        let b = next.to_v2();

        if b.x == a.x + 1 && b.y == a.y + 1 {
            // Going down the right rim.
            Some(Point2D::new(a.x + 1, a.y))
        } else if b.x == a.x - 1 && b.y == a.y - 1 {
            // Going up the left rim.
            Some(Point2D::new(a.x - 1, a.y))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(self.pos * (self.radius + 1) as f32 / self.radius as f32,
                        self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint { PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius) }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::iter::FromIterator;
    use euclid::Point2D;
    use super::{FovValue, HexFov};
    use hex::HexGeom;

    #[derive(PartialEq, Eq, Clone)]
    struct Cell1 {
        range: i32,
    }

    impl FovValue for Cell1 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }
    }

    #[derive(PartialEq, Eq, Clone)]
    struct Cell2 {
        range: i32,
    }

    impl FovValue for Cell2 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }

        fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
            let _ = offset;
            true
        }
    }

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let field: HashMap<Point2D<i32>, Cell1> = HashMap::from_iter(HexFov::new(Cell1 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(!field.contains_key(&Point2D::new(1, -1)));

        // Now test out the fake-isometric corners.
        let field: HashMap<Point2D<i32>, Cell2> = HashMap::from_iter(HexFov::new(Cell2 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(field.contains_key(&Point2D::new(1, -1)));
    }
}
<commit_msg>Get rid of the clone in HexFov<commit_after>use num::Integer;
use euclid::Point2D;
use hex::Dir6;

pub trait FovValue: Eq + Clone {
    /// Construct a new FovValue based on previous one and a new point in the fov.
    fn advance(&self, offset: Point2D<i32>) -> Option<Self>;

    /// Optional method for showing acute corners of fake isometric rooms in fov.
    fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
        let _ = offset;
        false
    }
}

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that maps cells into user-specified values and indicates when traversal should
/// stop.
pub struct HexFov<T> {
    stack: Vec<Sector<T>>,
    /// Extra values generated by special cases.
    side_channel: Vec<(Point2D<i32>, T)>,
}

impl<T: FovValue> HexFov<T> {
    pub fn new(init: T) -> HexFov<T> {
        // We could run f for (0, 0) here, but the traditional way for the FOV to work is to only
        // consider your surroundings, not the origin site itself.
        HexFov {
            stack: vec![Sector {
                            begin: PolarPoint::new(0.0, 1),
                            pt: PolarPoint::new(0.0, 1),
                            end: PolarPoint::new(6.0, 1),
                            prev_value: init.clone(),
                            group_value: None,
                        }],
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![(Point2D::new(0, 0), init)],
        }
    }
}

impl<T: FovValue> Iterator for HexFov<T> {
    type Item = (Point2D<i32>, T);
    fn next(&mut self) -> Option<(Point2D<i32>, T)> {
        // Empty the side channel before proceeding to the algorithm proper.
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        // Start processing the next sector in the stack.
        if let Some(mut current) = self.stack.pop() {
            // Generate the user value from the previous value in the stack item and the working
            // position in the current sector.
            let current_value = current.prev_value.advance(current.pt.to_v2());

            // Proceed along the sector if we're not at its end yet and the user value exists.
            if current.pt.is_below(current.end) && current_value.is_some() {
                let pos = current.pt.to_v2();
                let current_value = current_value.unwrap();

                match current.group_value {
                    None => {
                        // Beginning of group, value isn't set.
                        current.group_value = Some(current_value.clone());
                    }
                    Some(ref g) if g != &current_value => {
                        // Value changed, branch out.

                        // Add the rest of this sector with the new value.
                        self.stack.push(Sector {
                            begin: current.pt,
                            pt: current.pt,
                            end: current.end,
                            prev_value: current.prev_value,
                            group_value: Some(current_value.clone()),
                        });

                        // Branch further on the arc processed so far.
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            prev_value: g.clone(),
                            group_value: None,
                        });
                        return self.next();
                    }
                    _ => {}
                }

                // Current value and group value are assumed to be the same from here on.
                assert!(current.group_value == Some(current_value.clone()));

                // Hack for making acute corner tiles of fake-isometric rooms visible.

                // We're moving along a vertical line on the hex circle, so there are side
                // points to check.
                if let Some(side_pos) = current.pt.side_point() {

                    let next = current.pt.next();
                    // If the next cell is within the current span and the current cell is
                    // wallform,
                    if next.is_below(current.end) &&
                       current.prev_value.is_fake_isometric_wall(current.pt.to_v2()) {
                        // and if the next cell is visible,
                        if let Some(next_value) = current.prev_value.advance(next.to_v2()) {
                            // and if the current and the next cell are in the same value group,
                            // both the next cell and the third corner point cell are
                            // wallforms, and the side point would not be otherwise
                            // visible:
                            if next_value == current.prev_value &&
                               next_value.is_fake_isometric_wall(next.to_v2()) &&
                               current.prev_value.advance(side_pos).is_none() &&
                               current.prev_value.is_fake_isometric_wall(side_pos) {
                                // Add the side point to the side channel.
                                self.side_channel.push((side_pos, current.prev_value.clone()));
                            }
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some((pos, current_value));
            } else {
                // Hit the end of the sector, branch ahead.
                if let Some(value) = current.group_value {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        prev_value: value,
                        group_value: None,
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector<T> {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// The user value from previous iteration.
    prev_value: T,
    /// The user value for this group.
    group_value: Option<T>,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32,
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint {
        PolarPoint {
            pos: pos,
            radius: radius,
        }
    }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }

    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }

    pub fn to_v2(self) -> Point2D<i32> {
        if self.radius == 0 {
            return Point2D::new(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return the point outside of the circle between the two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<Point2D<i32>> {
        let next = self.next();
        let a = self.to_v2();
        let b = next.to_v2();

        if b.x == a.x + 1 && b.y == a.y + 1 {
            // Going down the right rim.
            Some(Point2D::new(a.x + 1, a.y))
        } else if b.x == a.x - 1 && b.y == a.y - 1 {
            // Going up the left rim.
            Some(Point2D::new(a.x - 1, a.y))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(self.pos * (self.radius + 1) as f32 / self.radius as f32,
                        self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint { PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius) }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::iter::FromIterator;
    use euclid::Point2D;
    use super::{FovValue, HexFov};
    use hex::HexGeom;

    #[derive(PartialEq, Eq, Clone)]
    struct Cell1 {
        range: i32,
    }

    impl FovValue for Cell1 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }
    }

    #[derive(PartialEq, Eq, Clone)]
    struct Cell2 {
        range: i32,
    }

    impl FovValue for Cell2 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }

        fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
            let _ = offset;
            true
        }
    }

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let field: HashMap<Point2D<i32>, Cell1> = HashMap::from_iter(HexFov::new(Cell1 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(!field.contains_key(&Point2D::new(1, -1)));

        // Now test out the fake-isometric corners.
        let field: HashMap<Point2D<i32>, Cell2> = HashMap::from_iter(HexFov::new(Cell2 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(field.contains_key(&Point2D::new(1, -1)));
    }
}
<|endoftext|>