<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;
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, ParseFromStreamError> {
    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 ctx via the
            // stream_ctx_close function
            Err(ParseFromStreamError::CouldNotCreateParser)
        } 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
}

fn xml2_error_to_string(xerr: xmlErrorPtr) -> String {
    unsafe {
        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)
            };

            format!(
                "Error domain {} code {} on line {} column {} of {}: {}",
                xerr.domain, xerr.code, xerr.line, xerr.int2, file, message
            )
        } else {
            // The error is not set?  Return a generic message :(
            "Error parsing XML data".to_string()
        }
    }
}

#[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 _);

    set_gerror(error, 0, &xml2_error_to_string(xerr));
}

// Error returned when parsing an XML stream
pub enum ParseFromStreamError {
    // We couldn't even create the libxml2 parser
    CouldNotCreateParser,

    // GIO error from the I/O callbacks
    IoError(glib::Error),

    // XML parsing error from libxml2
    XmlParseError(String),
}

// Parses XML from a stream into an XmlState.
//
// This can be called "in the middle" of an XmlState's processing status,
// for example, when including another XML file via xi:include.
pub fn xml_state_parse_from_stream(
    xml: &mut XmlState,
    unlimited_size: bool,
    stream: gio::InputStream,
    cancellable: Option<gio::Cancellable>,
) -> Result<(), ParseFromStreamError> {
    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) => unsafe {
            let xml_parse_success = xmlParseDocument(parser) == 0;

            let io_success = gio_err.is_null();

            let res;

            if !io_success {
                res = Err(ParseFromStreamError::IoError(from_glib_full(gio_err)));
            } else if !xml_parse_success {
                let xerr = xmlCtxtGetLastError(parser as *mut _);
                res = Err(ParseFromStreamError::XmlParseError(xml2_error_to_string(
                    xerr,
                )));
            } else {
                res = Ok(());
            }

            free_xml_parser_and_doc(parser);

            res
        },

        Err(e) => Err(e),
    }
}

#[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);

    match xml_state_parse_from_stream(xml, unlimited_size, stream, cancellable) {
        Ok(()) => true.to_glib(),

        Err(e) => {
            match e {
                ParseFromStreamError::CouldNotCreateParser => {
                    set_gerror(error, 0, "Error creating XML parser");
                }

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

                ParseFromStreamError::XmlParseError(s) => {
                    set_gerror(error, 0, &s);
                }
            }

            false.to_glib()
        }
    }
}
<commit_msg>Xml2Parser: Move the parse-from-stream machinery to a struct<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;
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
}

struct Xml2Parser {
    parser: xmlParserCtxtPtr,
    gio_error: *mut *mut glib_sys::GError,
}

impl Xml2Parser {
    fn from_stream(
        xml: &mut XmlState,
        unlimited_size: bool,
        stream: gio::InputStream,
        cancellable: Option<gio::Cancellable>,
        gio_error: *mut *mut glib_sys::GError,
    ) -> Result<Xml2Parser, ParseFromStreamError> {
        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 ctx via the
                // stream_ctx_close function
                Err(ParseFromStreamError::CouldNotCreateParser)
            } else {
                set_xml_parse_options(parser, unlimited_size);
                Ok(Xml2Parser { parser, gio_error })
            }
        }
    }

    fn parse(&self) -> Result<(), ParseFromStreamError> {
        unsafe {
            let xml_parse_success = xmlParseDocument(self.parser) == 0;

            let io_success = (*self.gio_error).is_null();

            if !io_success {
                Err(ParseFromStreamError::IoError(from_glib_full(
                    *self.gio_error,
                )))
            } else if !xml_parse_success {
                let xerr = xmlCtxtGetLastError(self.parser as *mut _);
                Err(ParseFromStreamError::XmlParseError(xml2_error_to_string(
                    xerr,
                )))
            } else {
                Ok(())
            }
        }
    }
}

impl Drop for Xml2Parser {
    fn drop(&mut self) {
        free_xml_parser_and_doc(self.parser);
        self.parser = ptr::null_mut();
    }
}

#[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
}

fn xml2_error_to_string(xerr: xmlErrorPtr) -> String {
    unsafe {
        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)
            };

            format!(
                "Error domain {} code {} on line {} column {} of {}: {}",
                xerr.domain, xerr.code, xerr.line, xerr.int2, file, message
            )
        } else {
            // The error is not set?  Return a generic message :(
            "Error parsing XML data".to_string()
        }
    }
}

#[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 _);

    set_gerror(error, 0, &xml2_error_to_string(xerr));
}

// Error returned when parsing an XML stream
pub enum ParseFromStreamError {
    // We couldn't even create the libxml2 parser
    CouldNotCreateParser,

    // GIO error from the I/O callbacks
    IoError(glib::Error),

    // XML parsing error from libxml2
    XmlParseError(String),
}

// Parses XML from a stream into an XmlState.
//
// This can be called "in the middle" of an XmlState's processing status,
// for example, when including another XML file via xi:include.
pub fn xml_state_parse_from_stream(
    xml: &mut XmlState,
    unlimited_size: bool,
    stream: gio::InputStream,
    cancellable: Option<gio::Cancellable>,
) -> Result<(), ParseFromStreamError> {
    let mut gio_err: *mut glib_sys::GError = ptr::null_mut();

    Xml2Parser::from_stream(xml, unlimited_size, stream, cancellable, &mut gio_err)
        .and_then(|parser| parser.parse())
}

#[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);

    match xml_state_parse_from_stream(xml, unlimited_size, stream, cancellable) {
        Ok(()) => true.to_glib(),

        Err(e) => {
            match e {
                ParseFromStreamError::CouldNotCreateParser => {
                    set_gerror(error, 0, "Error creating XML parser");
                }

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

                ParseFromStreamError::XmlParseError(s) => {
                    set_gerror(error, 0, &s);
                }
            }

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


<commit_before>#[macro_use]
extern crate clap;

use std::fs::File;
use std::io::{Read,Write};

// [Macro clap::clap_app](https://docs.rs/clap/2.33.0/clap/macro.clap_app.html)

fn gpu_freq_stat(tail: &str) -> String {
    format!("/sys/class/devfreq/gpufreq/{}", tail)
}

fn ddr_freq_stat(tail: &str) -> String {
    format!("/sys/class/devfreq/ddrfreq/{}", tail)
}

fn cpu_freq_stat(id: usize, tail: &str) -> String {
    format!("/sys/devices/system/cpu/cpu{}/cpufreq/{}", id, tail)
}

struct AvailableFrequencies
{
    gpu: Vec<u64>,
    ddr: Vec<u64>,
    cpu_little: Vec<u64>,
    cpu_medium: Vec<u64>,
    cpu_big: Vec<u64>,
}

struct ActualFrequencies
{
    gpu: u64,
    ddr: u64,
    cpu_little: u64,
    cpu_medium: u64,
    cpu_big: u64,
}

impl ActualFrequencies {
    fn cons() -> ActualFrequencies {
        ActualFrequencies { gpu: 0, ddr: 0, cpu_little: 0, cpu_medium: 0, cpu_big: 0, }
    }

    fn init(gpu: u64, ddr: u64, cpu_l: u64, cpu_m: u64, cpu_b: u64) -> ActualFrequencies {
        ActualFrequencies { gpu: gpu, ddr: ddr, cpu_little: cpu_l, cpu_medium: cpu_m, cpu_big: cpu_b, }
    }
}

fn read_sys_str(sys_path: &str) -> String {
    let mut read_buf = String::new();
    if let Ok(mut file) = File::open(sys_path) {
        let sz = file.read_to_string(&mut read_buf).expect("cannot read from sysfs");
        assert!(sz > 0);
    }
    read_buf.trim().to_string()
}

fn read_sys_as_u64(sys_path: &str) -> u64 {
    let str_buf = read_sys_str(sys_path);
    str_buf.parse().expect("cannot parse to u64")
}

fn write_sys_str(sys_path: &str, val: &str) {
    if let Ok(mut file) = File::create(sys_path) {
        let sz = file.write(&val.as_bytes()).expect("cannot write to sysfs");
        assert!(sz > 0);
    }
}

fn write_sys_u64(sys_path: &str, val: u64) {
    write_sys_str(sys_path, &format!("{}", val))
}

fn read_gpu_frequency() -> u64 {
    read_sys_as_u64(&gpu_freq_stat("cur_freq"))
}

fn get_device_available_frequencies() -> AvailableFrequencies {
    fn get_sys_freqs(sys_path: &str) -> Vec<u64> {
        let mut freqs: Vec<u64> = Vec::new();
        if let Ok(mut file) = File::open(sys_path) {
            let mut read_buf = String::new();
            let sz = file.read_to_string(&mut read_buf).expect("cannot read sysfs");
            assert!(sz > 0);
            let vec: Vec<&str> = read_buf.trim().split(' ').collect();
            freqs = vec.into_iter().map(|x| x.parse().expect("cannot parse to u64")).collect();
        }
        freqs
    }
    AvailableFrequencies { gpu: get_sys_freqs(&gpu_freq_stat("available_frequencies")),
                           ddr: Vec::new(), cpu_little: Vec::new(), cpu_medium: Vec::new(), cpu_big: Vec::new(), }
}

fn lock_sys_freq(freq: u64, cur_freq: u64, sys_min_freq: &str, sys_max_freq: &str)
{
    if freq < cur_freq {
        write_sys_u64(sys_min_freq, freq);
        write_sys_u64(sys_max_freq, freq)
    } else if freq > cur_freq {
        write_sys_u64(sys_max_freq, freq);
        write_sys_u64(sys_min_freq, freq)
    }
}

fn lock_gpu_frequency(freq: u64)
{
    lock_sys_freq(freq, read_gpu_frequency(), &gpu_freq_stat("min_freq"), &gpu_freq_stat("max_freq"))
}

fn main() {
    let matches = clap_app!(gf =>
        (version: "0.1")
        (author: "Alexander Samoilov <alexander.samoilov@gmail.com>")
        (about: "ARM Android cross-compile sample project")
        (@arg AVAILABLE: -a --available "display available frequencies")
        (@arg SHOW: -s --show "show current frequencies")
        (@arg SET_GPU: -g --gpu +takes_value "set GPU frequency")
        (@arg LOCK_FREQ: -l --lock ... +takes_value "lock GPU/DDR/CPU frequencies")
        (@arg VERBOSE: -v --verbose "be verbose")
    ).get_matches();

    if matches.is_present("VERBOSE") {
        println!("gf_freq command line args matches: {:?}", matches)
    }

    if matches.is_present("AVAILABLE") {
        let avail = get_device_available_frequencies();
        println!("GPU available frequencies: {:?}", avail.gpu)
    }

    if matches.is_present("SHOW") {
        println!("show device current frequencies: gpu: {}", read_gpu_frequency());
    }

    if let Ok(gpu_freq) = value_t!(matches, "SET_GPU", u64) {
        println!("Set GPU frequency to: {}", gpu_freq);
        lock_gpu_frequency(gpu_freq)
    }

    if let Ok(freq_vec) = values_t!(matches, "LOCK_FREQ", u64) {
        println!("freq_vec: {:?}", freq_vec);
        for v in freq_vec {
            println!("Set frequency to: {}", v)
        }
    }

}
<commit_msg>poller has been implemented.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Samoilov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#[macro_use]
extern crate clap;

use std::fs::File;
use std::io::{Read,Write};
use core::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;

// [Macro clap::clap_app](https://docs.rs/clap/2.33.0/clap/macro.clap_app.html)

fn gpu_freq_stat(tail: &str) -> String {
    format!("/sys/class/devfreq/gpufreq/{}", tail)
}

fn ddr_freq_stat(tail: &str) -> String {
    format!("/sys/class/devfreq/ddrfreq/{}", tail)
}

fn cpu_freq_stat(id: usize, tail: &str) -> String {
    format!("/sys/devices/system/cpu/cpu{}/cpufreq/{}", id, tail)
}

struct AvailableFrequencies
{
    gpu: Vec<u64>,
    ddr: Vec<u64>,
    cpu_little: Vec<u64>,
    cpu_medium: Vec<u64>,
    cpu_big: Vec<u64>,
}

struct ActualFrequencies
{
    gpu: u64,
    ddr: u64,
    cpu_little: u64,
    cpu_medium: u64,
    cpu_big: u64,
}

impl ActualFrequencies {
    fn zero() -> ActualFrequencies {
        ActualFrequencies { gpu: 0, ddr: 0, cpu_little: 0, cpu_medium: 0, cpu_big: 0, }
    }

    fn new(gpu: u64, ddr: u64, cpu_l: u64, cpu_m: u64, cpu_b: u64) -> ActualFrequencies {
        ActualFrequencies { gpu: gpu, ddr: ddr, cpu_little: cpu_l, cpu_medium: cpu_m, cpu_big: cpu_b, }
    }
}

impl std::cmp::PartialEq for ActualFrequencies {

    #[inline]
    fn eq(&self, other: &ActualFrequencies) -> bool {
        self.gpu == other.gpu &&
        self.ddr == other.ddr &&
        self.cpu_little == other.cpu_little &&
        self.cpu_medium == other.cpu_medium &&
        self.cpu_big == other.cpu_big
    }

    #[inline]
    fn ne(&self, other: &ActualFrequencies) -> bool {
        !self.eq(other)
    }
}

struct FrequencyLocker {
    freqs: ActualFrequencies,
    runnable: Arc<AtomicBool>,
}

impl FrequencyLocker {
    fn new(freq_vec: &Vec<u64>) -> FrequencyLocker {
        FrequencyLocker {
            freqs: ActualFrequencies::new(freq_vec[0], freq_vec[1], freq_vec[2], freq_vec[3], freq_vec[4]),
            runnable: Arc::new(AtomicBool::new(true)),
        }
    }

    fn do_poll(&self) {
        while self.runnable.load(Ordering::SeqCst) {
            std::thread::sleep(std::time::Duration::from_millis(50));
            let cur_freqs = read_current_frequencies();
            if cur_freqs != self.freqs {
                lock_all_frequencies(&self.freqs)
            }
        }
    }
}

#[inline]
fn read_sys_str(sys_path: &str) -> String {
    let mut read_buf = String::new();
    if let Ok(mut file) = File::open(sys_path) {
        let sz = file.read_to_string(&mut read_buf).expect("cannot read from sysfs");
        assert!(sz > 0);
    }
    read_buf.trim().to_string()
}

#[inline]
fn read_sys_as_u64(sys_path: &str) -> u64 {
    let str_buf = read_sys_str(sys_path);
    str_buf.parse().expect("cannot parse to u64")
}

#[inline]
fn write_sys_str(sys_path: &str, val: &str) {
    if let Ok(mut file) = File::create(sys_path) {
        let sz = file.write(&val.as_bytes()).expect("cannot write to sysfs");
        assert!(sz > 0);
    }
}

#[inline]
fn write_sys_u64(sys_path: &str, val: u64) {
    write_sys_str(sys_path, &format!("{}", val))
}

#[inline]
fn read_gpu_frequency() -> u64 {
    read_sys_as_u64(&gpu_freq_stat("cur_freq"))
}

fn get_device_available_frequencies() -> AvailableFrequencies {
    fn get_sys_freqs(sys_path: &str) -> Vec<u64> {
        let mut freqs: Vec<u64> = Vec::new();
        if let Ok(mut file) = File::open(sys_path) {
            let mut read_buf = String::new();
            let sz = file.read_to_string(&mut read_buf).expect("cannot read sysfs");
            assert!(sz > 0);
            let vec: Vec<&str> = read_buf.trim().split(' ').collect();
            freqs = vec.into_iter().map(|x| x.parse().expect("cannot parse to u64")).collect();
        }
        freqs
    }
    AvailableFrequencies { gpu: get_sys_freqs(&gpu_freq_stat("available_frequencies")),
                           ddr: Vec::new(),
                           cpu_little: Vec::new(),
                           cpu_medium: Vec::new(),
                           cpu_big: Vec::new(), }
}

#[inline]
fn read_current_frequencies() -> ActualFrequencies {
    ActualFrequencies {
        gpu: read_gpu_frequency(),
        ddr: 0,
        cpu_little: 0,
        cpu_medium: 0,
        cpu_big: 0,
    }
}

#[inline]
fn lock_sys_freq(freq: u64, cur_freq: u64, sys_min_freq: &str, sys_max_freq: &str)
{
    if freq < cur_freq {
        write_sys_u64(sys_min_freq, freq);
        write_sys_u64(sys_max_freq, freq)
    } else if freq > cur_freq {
        write_sys_u64(sys_max_freq, freq);
        write_sys_u64(sys_min_freq, freq)
    }
}

#[inline]
fn lock_gpu_frequency(freq: u64)
{
    lock_sys_freq(freq, read_gpu_frequency(), &gpu_freq_stat("min_freq"), &gpu_freq_stat("max_freq"))
}

#[inline]
fn lock_all_frequencies(freqs: &ActualFrequencies)
{
    lock_gpu_frequency(freqs.gpu);
}

fn main() {
    let matches = clap_app!(gf =>
        (version: "0.2")
        (author: "Alexander Samoilov <alexander.samoilov@gmail.com>")
        (about: "ARM Android cross-compile sample project")
        (@arg AVAILABLE: -a --available "display available frequencies")
        (@arg SHOW: -s --show "show current frequencies")
        (@arg SET_GPU: -g --gpu +takes_value "set GPU frequency")
        (@arg LOCK_FREQ: -l --lock ... +takes_value "lock GPU/DDR/CPU frequencies")
        (@arg VERBOSE: -v --verbose "be verbose")
    ).get_matches();

    if matches.is_present("VERBOSE") {
        println!("gf_freq command line args matches: {:?}", matches)
    }

    if matches.is_present("AVAILABLE") {
        let avail = get_device_available_frequencies();
        println!("GPU available frequencies: {:?}", avail.gpu)
    }

    if matches.is_present("SHOW") {
        println!("show device current frequencies: gpu: {}", read_gpu_frequency())
    }

    if let Ok(gpu_freq) = value_t!(matches, "SET_GPU", u64) {
        println!("Set GPU frequency to: {}", gpu_freq);
        lock_gpu_frequency(gpu_freq)
    }

    if let Ok(freq_vec) = values_t!(matches, "LOCK_FREQ", u64) {
        println!("freq_vec: {:?}", freq_vec);
        for v in freq_vec.clone() {
            println!("Set frequency to: {}", v)
        }

        let freq_locker = FrequencyLocker::new(&freq_vec);
        let poller = thread::spawn(move || {
            freq_locker.do_poll()
        });
        poller.join().expect("frequency locker thread has panicked");
    }

}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::activation::Activatable;
use dom::attr::AttrValue;
use dom::bindings::codegen::Bindings::HTMLLabelElementBinding;
use dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
use dom::bindings::conversions::Castable;
use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::node::{document_from_node, Node};
use dom::virtualmethods::VirtualMethods;
use string_cache::Atom;
use util::str::DOMString;

#[dom_struct]
pub struct HTMLLabelElement {
    htmlelement: HTMLElement,
}

impl HTMLLabelElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLLabelElement {
        HTMLLabelElement {
            htmlelement:
                HTMLElement::new_inherited(localName, prefix, document)
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLLabelElement> {
        let element = HTMLLabelElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLLabelElementBinding::Wrap)
    }
}

impl Activatable for HTMLLabelElement {
    fn as_element(&self) -> &Element {
        self.upcast::<Element>()
    }

    fn is_instance_activatable(&self) -> bool {
        return true;
    }

    // https://html.spec.whatwg.org/multipage/#run-pre-click-activation-steps
    // https://html.spec.whatwg.org/multipage/#the-button-element:activation-behavior
    fn pre_click_activation(&self) {
    }

    // https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
    fn canceled_activation(&self) {
    }

    // https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
    fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
        self.upcast::<Element>()
            .as_maybe_activatable()
            .map(|a| a.synthetic_click_activation(false, false, false, false));
    }

    // https://html.spec.whatwg.org/multipage/#implicit-submission
    fn implicit_submission(&self, ctrlKey: bool, shiftKey: bool, altKey: bool, metaKey: bool) {
        //FIXME: Investigate and implement implicit submission for label elements
        // Issue filed at https://github.com/servo/servo/issues/8263
    }

}

impl HTMLLabelElementMethods for HTMLLabelElement {
    // https://html.spec.whatwg.org/multipage/#dom-fae-form
    fn GetForm(&self) -> Option<Root<HTMLFormElement>> {
        self.form_owner()
    }

    // https://html.spec.whatwg.org/multipage/#dom-label-htmlfor
    make_getter!(HtmlFor, "for");

    // https://html.spec.whatwg.org/multipage/#dom-label-htmlfor
    make_atomic_setter!(SetHtmlFor, "for");

    // https://html.spec.whatwg.org/multipage/#dom-label-control
    fn GetControl(&self) -> Option<Root<HTMLElement>> {
        if !self.upcast::<Node>().is_in_doc() {
            return None;
        }

        let for_attr = match self.upcast::<Element>().get_attribute(&ns!(""), &atom!("for")) {
            Some(for_attr) => for_attr,
            None => return self.first_labelable_descendant(),
        };

        let for_value = for_attr.value();
        document_from_node(self).get_element_by_id(for_value.as_atom())
                                .and_then(Root::downcast::<HTMLElement>)
                                .into_iter()
                                .filter(|e| e.is_labelable_element())
                                .next()
    }
}

impl VirtualMethods for HTMLLabelElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("for") => AttrValue::from_atomic(value),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl HTMLLabelElement {
    fn first_labelable_descendant(&self) -> Option<Root<HTMLElement>> {
        self.upcast::<Node>()
            .traverse_preorder()
            .filter_map(Root::downcast::<HTMLElement>)
            .filter(|elem| elem.is_labelable_element())
            .next()
    }
}

impl FormControl for HTMLLabelElement {}
<commit_msg>Auto merge of #8287 - eefriedman:label-unused-var, r=Manishearth<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::activation::Activatable;
use dom::attr::AttrValue;
use dom::bindings::codegen::Bindings::HTMLLabelElementBinding;
use dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
use dom::bindings::conversions::Castable;
use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::node::{document_from_node, Node};
use dom::virtualmethods::VirtualMethods;
use string_cache::Atom;
use util::str::DOMString;

#[dom_struct]
pub struct HTMLLabelElement {
    htmlelement: HTMLElement,
}

impl HTMLLabelElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLLabelElement {
        HTMLLabelElement {
            htmlelement:
                HTMLElement::new_inherited(localName, prefix, document)
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLLabelElement> {
        let element = HTMLLabelElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLLabelElementBinding::Wrap)
    }
}

impl Activatable for HTMLLabelElement {
    fn as_element(&self) -> &Element {
        self.upcast::<Element>()
    }

    fn is_instance_activatable(&self) -> bool {
        return true;
    }

    // https://html.spec.whatwg.org/multipage/#run-pre-click-activation-steps
    // https://html.spec.whatwg.org/multipage/#the-button-element:activation-behavior
    fn pre_click_activation(&self) {
    }

    // https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
    fn canceled_activation(&self) {
    }

    // https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
    fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
        self.upcast::<Element>()
            .as_maybe_activatable()
            .map(|a| a.synthetic_click_activation(false, false, false, false));
    }

    // https://html.spec.whatwg.org/multipage/#implicit-submission
    fn implicit_submission(&self, _ctrlKey: bool, _shiftKey: bool, _altKey: bool, _metaKey: bool) {
        //FIXME: Investigate and implement implicit submission for label elements
        // Issue filed at https://github.com/servo/servo/issues/8263
    }

}

impl HTMLLabelElementMethods for HTMLLabelElement {
    // https://html.spec.whatwg.org/multipage/#dom-fae-form
    fn GetForm(&self) -> Option<Root<HTMLFormElement>> {
        self.form_owner()
    }

    // https://html.spec.whatwg.org/multipage/#dom-label-htmlfor
    make_getter!(HtmlFor, "for");

    // https://html.spec.whatwg.org/multipage/#dom-label-htmlfor
    make_atomic_setter!(SetHtmlFor, "for");

    // https://html.spec.whatwg.org/multipage/#dom-label-control
    fn GetControl(&self) -> Option<Root<HTMLElement>> {
        if !self.upcast::<Node>().is_in_doc() {
            return None;
        }

        let for_attr = match self.upcast::<Element>().get_attribute(&ns!(""), &atom!("for")) {
            Some(for_attr) => for_attr,
            None => return self.first_labelable_descendant(),
        };

        let for_value = for_attr.value();
        document_from_node(self).get_element_by_id(for_value.as_atom())
                                .and_then(Root::downcast::<HTMLElement>)
                                .into_iter()
                                .filter(|e| e.is_labelable_element())
                                .next()
    }
}

impl VirtualMethods for HTMLLabelElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("for") => AttrValue::from_atomic(value),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl HTMLLabelElement {
    fn first_labelable_descendant(&self) -> Option<Root<HTMLElement>> {
        self.upcast::<Node>()
            .traverse_preorder()
            .filter_map(Root::downcast::<HTMLElement>)
            .filter(|elem| elem.is_labelable_element())
            .next()
    }
}

impl FormControl for HTMLLabelElement {}
<|endoftext|>


<commit_before>use smallvec::SmallVec;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::Infallible;
use std::fmt::Debug;

/// Partial variant of `std::iter::FromIterator`.
///
/// This trait is implemented for types which can be constructed from an iterator of decoded SSZ
/// values, but which may refuse values once a length limit is reached.
pub trait TryFromIter<T>: Sized {
    type Error: Debug;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>;
}

// It would be nice to be able to do a blanket impl, e.g.
//
// `impl TryFromIter<T> for C where C: FromIterator<T>`
//
// However this runs into trait coherence issues due to the type parameter `T` on `TryFromIter`.
//
// E.g. If we added an impl downstream for `List<T, N>` then another crate downstream of that
// could legally add an impl of `FromIterator<Local> for List<Local, N>` which would create
// two conflicting implementations for `List<Local, N>`. Hence the `List<T, N>` impl is disallowed
// by the compiler in the presence of the blanket impl. That's obviously annoying, so we opt to
// abandon the blanket impl in favour of impls for selected types.
impl<T> TryFromIter<T> for Vec<T> {
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>,
    {
        Ok(Self::from_iter(iter))
    }
}

impl<T, const N: usize> TryFromIter<T> for SmallVec<[T; N]> {
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>,
    {
        Ok(Self::from_iter(iter))
    }
}

impl<K, V> TryFromIter<(K, V)> for BTreeMap<K, V>
where
    K: Ord,
{
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = (K, V)>,
    {
        Ok(Self::from_iter(iter))
    }
}

impl<T> TryFromIter<T> for BTreeSet<T>
where
    T: Ord,
{
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>,
    {
        Ok(Self::from_iter(iter))
    }
}

/// Partial variant of `collect`.
pub trait TryCollect: Iterator {
    fn try_collect<C>(self) -> Result<C, C::Error>
    where
        C: TryFromIter<Self::Item>;
}

impl<I> TryCollect for I
where
    I: Iterator,
{
    fn try_collect<C>(self) -> Result<C, C::Error>
    where
        C: TryFromIter<Self::Item>,
    {
        C::try_from_iter(self)
    }
}
<commit_msg>Pre-allocate vectors in SSZ decoding (#3417)<commit_after>use smallvec::SmallVec;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::Infallible;
use std::fmt::Debug;

/// Partial variant of `std::iter::FromIterator`.
///
/// This trait is implemented for types which can be constructed from an iterator of decoded SSZ
/// values, but which may refuse values once a length limit is reached.
pub trait TryFromIter<T>: Sized {
    type Error: Debug;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>;
}

// It would be nice to be able to do a blanket impl, e.g.
//
// `impl TryFromIter<T> for C where C: FromIterator<T>`
//
// However this runs into trait coherence issues due to the type parameter `T` on `TryFromIter`.
//
// E.g. If we added an impl downstream for `List<T, N>` then another crate downstream of that
// could legally add an impl of `FromIterator<Local> for List<Local, N>` which would create
// two conflicting implementations for `List<Local, N>`. Hence the `List<T, N>` impl is disallowed
// by the compiler in the presence of the blanket impl. That's obviously annoying, so we opt to
// abandon the blanket impl in favour of impls for selected types.
impl<T> TryFromIter<T> for Vec<T> {
    type Error = Infallible;

    fn try_from_iter<I>(values: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>,
    {
        // Pre-allocate the expected size of the Vec, which is parsed from the SSZ input bytes as
        // `num_items`. This length has already been checked to be less than or equal to the type's
        // maximum length in `decode_list_of_variable_length_items`.
        let iter = values.into_iter();
        let (_, opt_max_len) = iter.size_hint();
        let mut vec = Vec::with_capacity(opt_max_len.unwrap_or(0));
        vec.extend(iter);
        Ok(vec)
    }
}

impl<T, const N: usize> TryFromIter<T> for SmallVec<[T; N]> {
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>,
    {
        Ok(Self::from_iter(iter))
    }
}

impl<K, V> TryFromIter<(K, V)> for BTreeMap<K, V>
where
    K: Ord,
{
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = (K, V)>,
    {
        Ok(Self::from_iter(iter))
    }
}

impl<T> TryFromIter<T> for BTreeSet<T>
where
    T: Ord,
{
    type Error = Infallible;

    fn try_from_iter<I>(iter: I) -> Result<Self, Self::Error>
    where
        I: IntoIterator<Item = T>,
    {
        Ok(Self::from_iter(iter))
    }
}

/// Partial variant of `collect`.
pub trait TryCollect: Iterator {
    fn try_collect<C>(self) -> Result<C, C::Error>
    where
        C: TryFromIter<Self::Item>;
}

impl<I> TryCollect for I
where
    I: Iterator,
{
    fn try_collect<C>(self) -> Result<C, C::Error>
    where
        C: TryFromIter<Self::Item>,
    {
        C::try_from_iter(self)
    }
}
<|endoftext|>


<commit_before>use containers::hashset::Hash;
use containers::hashset::HashPrimeHelper;
use containers::array::Array;
use containers::list::List;
use containers::reference::Ref;
use containers::vector::Vector;
use memory::page::Page;
use memory::region::Region;

#[derive(Copy, Clone)]
pub struct HashMap<K: Hash<K> + Copy> {
    slots: Ref<Vector<Ref<List<Slot<K>>>>>,
}

impl<K: Hash<K> + Copy> HashMap<K> {
    pub fn from_vector(_pr: &Region, _rp: *mut Page, vector: Ref<Vector<K>>) -> Ref<HashMap<K>> {
        let _r = Region::create(_pr);
        let hash_size = HashPrimeHelper::get_prime(vector.length);
        let mut array: Ref<Array<Ref<List<Slot<K>>>>> = Ref::new(_r.page, Array::new());
        for _ in 0..hash_size {
            array.add(Ref::new(_r.page, List::new()));
        }
        let slots = Ref::new(_rp, Vector::from_array(_rp, array));
        let mut hash_map = Ref::new(_rp, HashMap { slots: slots });
        hash_map.initialize_from_vector(vector);
        hash_map
    }

    fn initialize_from_vector(&mut self, vector: Ref<Vector<K>>) {
        for value in vector.iter() {
            self.add(value);
        }
    }

    fn add(&mut self, value: &K) {
        let hash_code = value.hash();
        let slot_number = hash_code % self.slots.length;
        let mut slot_list = self.slots[slot_number];
        for slot in slot_list.get_iterator() {
            if value.equals(&slot.value) {
                return;
            }
        }

        slot_list.add(Slot {
            hash_code: hash_code,
            value: *value,
        });
    }

    pub fn contains(&self, value: K) -> bool {
        for slot in self.slots[value.hash() % self.slots.length].get_iterator() {
            if value.equals(&slot.value) {
                return true;
            }
        }

        false
    }
}

#[derive(Copy, Clone)]
struct Slot<K: Copy> {
    value: K,
    hash_code: usize,
}

#[test]
fn test_hash_map() {
    use containers::String;
    use memory::Heap;
    use memory::Page;
    use memory::Region;
    use memory::StackBucket;
    let mut heap = Heap::create();
    let root_stack_bucket = StackBucket::create(&mut heap);
    let root_page = Page::get(root_stack_bucket as usize);
    let _r = Region::create_from_page(root_page);
    let keywords = HashMap::from_vector(
        &_r,
        _r.page,
        Ref::new(
            _r.page,
            Vector::from_raw_array(
                _r.page,
                &[
                    String::from_string_slice(_r.page, "using"),
                    String::from_string_slice(_r.page, "namespace"),
                    String::from_string_slice(_r.page, "typedef"),
                ],
            ),
        ),
    );

    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "using")),
        true
    );
    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "namespace")),
        true
    );
    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "typedef")),
        true
    );
    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "nix")),
        false
    );
}
<commit_msg>HashMap of K, V<commit_after>use containers::hashset::Hash;
use containers::hashset::HashPrimeHelper;
use containers::array::Array;
use containers::list::List;
use containers::reference::Ref;
use containers::vector::Vector;
use memory::page::Page;
use memory::region::Region;

#[derive(Copy, Clone)]
pub struct KeyValuePair<K: Hash<K> + Copy, V: Copy> {
    key: K,
    value: V
}

#[derive(Copy, Clone)]
pub struct HashMap<K: Hash<K> + Copy, V: Copy> {
    slots: Ref<Vector<Ref<List<Slot<KeyValuePair<K, V>>>>>>,
}

impl<K: Hash<K> + Copy, V: Copy> HashMap<K, V> {
    pub fn from_vector(_pr: &Region, _rp: *mut Page, vector: Ref<Vector<KeyValuePair<K, V>>>) -> Ref<HashMap<K, V>> {
        let _r = Region::create(_pr);
        let hash_size = HashPrimeHelper::get_prime(vector.length);
        let mut array: Ref<Array<Ref<List<Slot<KeyValuePair<K, V>>>>>> = Ref::new(_r.page, Array::new());
        for _ in 0..hash_size {
            array.add(Ref::new(_r.page, List::new()));
        }
        let slots = Ref::new(_rp, Vector::from_array(_rp, array));
        let mut hash_map = Ref::new(_rp, HashMap { slots: slots });
        hash_map.initialize_from_vector(vector);
        hash_map
    }

    fn initialize_from_vector(&mut self, vector: Ref<Vector<KeyValuePair<K, V>>>) {
        for key_value_pair in vector.iter() {
            self.add(&key_value_pair.key, &key_value_pair.value);
        }
    }

    fn add(&mut self, key: &K, value: &V) {
        let hash_code = key.hash();
        let slot_number = hash_code % self.slots.length;
        let mut slot_list = self.slots[slot_number];
        for slot in slot_list.get_iterator() {
            if key.equals(&slot.value.key) {
                return;
            }
        }

        slot_list.add(Slot {
            hash_code: hash_code,
            value: KeyValuePair { key: *key, value: *value},
        });
    }

    pub fn contains(&self, value: K) -> bool {
        for slot in self.slots[value.hash() % self.slots.length].get_iterator() {
            if value.equals(&slot.value.key) {
                return true;
            }
        }

        false
    }
}

#[derive(Copy, Clone)]
struct Slot<K: Copy> {
    value: K,
    hash_code: usize,
}

#[test]
fn test_hash_map() {
    use containers::String;
    use memory::Heap;
    use memory::Page;
    use memory::Region;
    use memory::StackBucket;
    let mut heap = Heap::create();
    let root_stack_bucket = StackBucket::create(&mut heap);
    let root_page = Page::get(root_stack_bucket as usize);
    let _r = Region::create_from_page(root_page);
    let keywords = HashMap::from_vector(
        &_r,
        _r.page,
        Ref::new(
            _r.page,
            Vector::from_raw_array(
                _r.page,
                &[
                    String::from_string_slice(_r.page, "using"),
                    String::from_string_slice(_r.page, "namespace"),
                    String::from_string_slice(_r.page, "typedef"),
                ],
            ),
        ),
    );

    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "using")),
        true
    );
    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "namespace")),
        true
    );
    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "typedef")),
        true
    );
    assert_eq!(
        (*keywords).contains(String::from_string_slice(_r.page, "nix")),
        false
    );
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;

use crate::{
    prelude::*,
    render::RenderContext2D,
    utils,
    utils::{Brush, Point, Rectangle, Thickness},
};

pub struct RectangleRenderObject;

impl RectangleRenderObject {
    // Renders rectangle with border and without radius.
    fn render_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        render_context_2_d.rect(rect.x, rect.y, rect.width, rect.height);

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Builds rectangle path with radius and without border.
    fn render_rounded_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        let r = x + width;
        let b = y + height;
        render_context_2_d.move_to(x + radius, y);
        render_context_2_d.line_to(r - radius, y);
        render_context_2_d.quadratic_curve_to(r, y, r, y + radius);
        render_context_2_d.line_to(r, y + height - radius);
        render_context_2_d.quadratic_curve_to(r, b, r - radius, b);
        render_context_2_d.line_to(x + radius, b);
        render_context_2_d.quadratic_curve_to(x, b, x, b - radius);
        render_context_2_d.line_to(x, y + radius);
        render_context_2_d.quadratic_curve_to(x, y, x + radius, y);
        render_context_2_d.close_path();
    }

    fn render_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        render_context_2_d.move_to(x, y);
        render_context_2_d.arc(x + width / 2.0, y + height / 2.0, radius, 0.0, 2. * PI);
        render_context_2_d.close_path();
    }

    fn render_bordered_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_circle(render_context_2_d, x, y, width, height, radius);

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Renders rectangle with border and radius.
    fn render_rounded_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_rounded_rect_path(
            render_context_2_d,
            rect.x,
            rect.y,
            rect.width,
            rect.height,
            radius,
        );

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }
}

impl Into<Box<dyn RenderObject>> for RectangleRenderObject {
    fn into(self) -> Box<dyn RenderObject> {
        Box::new(self)
    }
}

impl RenderObject for RectangleRenderObject {
    fn render_self(&self, ctx: &mut Context<'_>, global_position: &Point) {
        let (bounds, background, border_radius, border_thickness, border_brush) = {
            let widget = ctx.widget();
            (
                widget.clone::<Rectangle>("bounds"),
                widget.get::<Brush>("background").clone(),
                widget.clone_or_default::<f64>("border_radius"),
                widget.clone_or_default::<Thickness>("border_width"),
                widget.clone_or_default::<Brush>("border_brush"),
            )
        };

        if (bounds.width() == 0.0
            || bounds.height() == 0.0
            || (background.is_transparent() && border_brush.is_transparent()))
            && (border_thickness.left == 0.0
                && border_thickness.top == 0.0
                && border_thickness.right == 0.0
                && border_thickness.bottom == 0.0)
        {
            return;
        }

        let has_thickness = border_thickness.left > 0.0
            || border_thickness.top > 0.0
            || border_thickness.right > 0.0
            || border_thickness.bottom > 0.0;

        ctx.render_context_2_d().begin_path();

        if bounds.width() == bounds.height() && border_radius >= bounds.width() / 2.0 {
            if !has_thickness {
                self.render_circle(
                    ctx.render_context_2_d(),
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                );
                ctx.render_context_2_d().set_fill_style(background);
                ctx.render_context_2_d().fill();
            } else {
                self.render_bordered_circle(
                    ctx.render_context_2_d(),
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                    background,
                    border_brush,
                    border_thickness,
                );
            }
            return;
        } else if border_radius > 0. && has_thickness {
            self.render_rounded_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                ),
                border_radius,
                background,
                border_brush,
                border_thickness,
            );
        } else if border_radius > 0. {
            self.render_rounded_rect_path(
                ctx.render_context_2_d(),
                global_position.x + bounds.x(),
                global_position.y + bounds.y(),
                bounds.width(),
                bounds.height(),
                border_radius,
            );

            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        } else if has_thickness {
            self.render_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                ),
                background,
                border_brush,
                border_thickness,
            );
        } else {
            ctx.render_context_2_d().rect(
                global_position.x + bounds.x(),
                global_position.y + bounds.y(),
                bounds.width(),
                bounds.height(),
            );
            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        }
    }
}
<commit_msg>Add a basic overlap avoidance algorithm to border-radius<commit_after>use std::f64::consts::PI;

use crate::{
    prelude::*,
    render::RenderContext2D,
    utils,
    utils::{Brush, Point, Rectangle, Thickness},
};

pub struct RectangleRenderObject;

impl RectangleRenderObject {
    // Renders rectangle with border and without radius.
    fn render_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        render_context_2_d.rect(rect.x, rect.y, rect.width, rect.height);

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Builds rectangle path with radius and without border.
    fn render_rounded_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        let r = x + width;
        let b = y + height;
        render_context_2_d.move_to(x + radius, y);
        render_context_2_d.line_to(r - radius, y);
        render_context_2_d.quadratic_curve_to(r, y, r, y + radius);
        render_context_2_d.line_to(r, y + height - radius);
        render_context_2_d.quadratic_curve_to(r, b, r - radius, b);
        render_context_2_d.line_to(x + radius, b);
        render_context_2_d.quadratic_curve_to(x, b, x, b - radius);
        render_context_2_d.line_to(x, y + radius);
        render_context_2_d.quadratic_curve_to(x, y, x + radius, y);
        render_context_2_d.close_path();
    }

    fn render_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        render_context_2_d.move_to(x, y);
        render_context_2_d.arc(x + width / 2.0, y + height / 2.0, radius, 0.0, 2. * PI);
        render_context_2_d.close_path();
    }

    fn render_bordered_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_circle(render_context_2_d, x, y, width, height, radius);

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Renders rectangle with border and radius.
    fn render_rounded_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_rounded_rect_path(
            render_context_2_d,
            rect.x,
            rect.y,
            rect.width,
            rect.height,
            radius,
        );

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    pub(crate) fn ensure_no_corner_overlap(
        border_radius: &mut f64,
        bounds: Rectangle
    ) {
        let mut ratio = 1.0;
        let sum = 2.0 * *border_radius;
        if bounds.width() < sum {
            ratio = f64::min(ratio, bounds.width() / sum);
        }
        if bounds.height() < sum {
            ratio = f64::min(ratio, bounds.height() / sum);
}
        if ratio < 1. {
            *border_radius *= ratio;
        }
    }
}

impl Into<Box<dyn RenderObject>> for RectangleRenderObject {
    fn into(self) -> Box<dyn RenderObject> {
        Box::new(self)
    }
}

impl RenderObject for RectangleRenderObject {
    fn render_self(&self, ctx: &mut Context<'_>, global_position: &Point) {
        let (bounds, background, border_radius, border_thickness, border_brush) = {
            let widget = ctx.widget();
            (
                widget.clone::<Rectangle>("bounds"),
                widget.get::<Brush>("background").clone(),
                widget.clone_or_default::<f64>("border_radius"),
                widget.clone_or_default::<Thickness>("border_width"),
                widget.clone_or_default::<Brush>("border_brush"),
            )
        };

        if (bounds.width() == 0.0
            || bounds.height() == 0.0
            || (background.is_transparent() && border_brush.is_transparent()))
            && (border_thickness.left == 0.0
                && border_thickness.top == 0.0
                && border_thickness.right == 0.0
                && border_thickness.bottom == 0.0)
        {
            return;
        }

        let has_thickness = border_thickness.left > 0.0
            || border_thickness.top > 0.0
            || border_thickness.right > 0.0
            || border_thickness.bottom > 0.0;

        ctx.render_context_2_d().begin_path();
        let mut border_radius = border_radius;
        Self::ensure_no_corner_overlap(&mut border_radius, bounds);
        if bounds.width() == bounds.height() {
            if !has_thickness {
                self.render_circle(
                    ctx.render_context_2_d(),
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                );
                ctx.render_context_2_d().set_fill_style(background);
                ctx.render_context_2_d().fill();
            } else {
                self.render_bordered_circle(
                    ctx.render_context_2_d(),
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                    background,
                    border_brush,
                    border_thickness,
                );
            }
            return;
        } else if border_radius > 0. && has_thickness {
            self.render_rounded_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                ),
                border_radius,
                background,
                border_brush,
                border_thickness,
            );
        } else if border_radius > 0. {
            self.render_rounded_rect_path(
                ctx.render_context_2_d(),
                global_position.x + bounds.x(),
                global_position.y + bounds.y(),
                bounds.width(),
                bounds.height(),
                border_radius,
            );

            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        } else if has_thickness {
            self.render_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    global_position.x + bounds.x(),
                    global_position.y + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                ),
                background,
                border_brush,
                border_thickness,
            );
        } else {
            ctx.render_context_2_d().rect(
                global_position.x + bounds.x(),
                global_position.y + bounds.y(),
                bounds.width(),
                bounds.height(),
            );
            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        }
    }
}
<|endoftext|>


<commit_before>use core::*;
use lexer::*;
use util::*;

#[derive(Debug)]
pub struct AlphabetScanner;

impl AlphabetScanner {
    fn is_first_name_letter(&self, c: u8) -> bool {
        (c >= b'a' && c <= b'z')
    }
}

impl Scanner for AlphabetScanner {
    fn scan(&self, state: State, c: u8) -> ScanResult {
        match state {
            State::Normal => {
                match c {
                    _ if self.is_first_name_letter(c) => push_into_mode!(InnerName),
                    _ => go_ahead!(),
                }
            },
            State::InnerName => {
                match c {
                    0 => finish!(),
                    _ if is_whitespace(c) => finish!(),
                    _ if is_token_end_delimiter(c) => delimite!(),
                    _ if is_alphanumeric(c) => push!(),
                    _ => ScanResult::Error,
                }
            },
            _ => go_ahead!(),
        }
    }

    fn return_token(&self, state: State, token_string: String) -> Option<Tokn> {
        match state {
            State::InnerName => Some(Tokn::Chvc(token_string)),
            _ => None,
        }
    }
}

#[test]
// #[ignore]
fn character_vector() {
    let scanners: &mut Vec<&Scanner> = &mut vec![&AlphabetScanner];
    lex_from_str("abc", "Chvc<abc>", scanners);
}
<commit_msg>Name can hyphen in contents(use for function name)<commit_after>use core::*;
use lexer::*;
use util::*;

#[derive(Debug)]
pub struct AlphabetScanner;

impl AlphabetScanner {
    fn is_first_name_letter(&self, c: u8) -> bool {
        (c >= b'a' && c <= b'z')
    }
}

impl Scanner for AlphabetScanner {
    fn scan(&self, state: State, c: u8) -> ScanResult {
        match state {
            State::Normal => {
                match c {
                    _ if self.is_first_name_letter(c) => push_into_mode!(InnerName),
                    _ => go_ahead!(),
                }
            },
            State::InnerName => {
                match c {
                    0 => finish!(),
                    _ if is_whitespace(c) => finish!(),
                    _ if is_token_end_delimiter(c) => delimite!(),
                    _ if is_alphanumeric(c) => push!(),
                    b'-' => push!(),
                    _ => ScanResult::Error,
                }
            },
            _ => go_ahead!(),
        }
    }

    fn return_token(&self, state: State, token_string: String) -> Option<Tokn> {
        match state {
            State::InnerName => Some(Tokn::Chvc(token_string)),
            _ => None,
        }
    }
}

#[test]
// #[ignore]
fn character_vector() {
    let scanners: &mut Vec<&Scanner> = &mut vec![&AlphabetScanner];
    lex_from_str("abc", "Chvc<abc>", scanners);
}
<|endoftext|>