<commit_before>/*
 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
 * Copyright (C) 2008 Apple Inc. All rights reserved.
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#include "core/svg/SVGAnimateElement.h"

#include "CSSPropertyNames.h"
#include "SVGNames.h"
#include "core/css/CSSParser.h"
#include "core/css/StylePropertySet.h"
#include "core/dom/QualifiedName.h"
#include "core/svg/SVGAnimatorFactory.h"

namespace WebCore {

SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* document)
    : SVGAnimationElement(tagName, document)
    , m_animatedPropertyType(AnimatedString)
{
    ASSERT(hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::setTag) || hasTagName(SVGNames::animateColorTag) || hasTagName(SVGNames::animateTransformTag));
    ScriptWrappable::init(this);
}

PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document* document)
{
    return adoptRef(new SVGAnimateElement(tagName, document));
}

SVGAnimateElement::~SVGAnimateElement()
{
}

bool SVGAnimateElement::hasValidAttributeType()
{
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return false;

    return m_animatedPropertyType != AnimatedUnknown && !hasInvalidCSSAttributeType();
}

AnimatedPropertyType SVGAnimateElement::determineAnimatedPropertyType(SVGElement* targetElement) const
{
    ASSERT(targetElement);

    Vector<AnimatedPropertyType> propertyTypes;
    targetElement->animatedPropertyTypeForAttribute(attributeName(), propertyTypes);
    if (propertyTypes.isEmpty())
        return AnimatedUnknown;

    ASSERT(propertyTypes.size() <= 2);
    AnimatedPropertyType type = propertyTypes[0];
    if (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor)
        return AnimatedUnknown;

    // Animations of transform lists are not allowed for <animate> or <set>
    // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
    if (type == AnimatedTransformList && !hasTagName(SVGNames::animateTransformTag))
        return AnimatedUnknown;

    // Fortunately there's just one special case needed here: SVGMarkerElements orientAttr, which
    // corresponds to SVGAnimatedAngle orientAngle and SVGAnimatedEnumeration orientType. We have to
    // figure out whose value to change here.
    if (targetElement->hasTagName(SVGNames::markerTag) && type == AnimatedAngle) {
        ASSERT(propertyTypes.size() == 2);
        ASSERT(propertyTypes[0] == AnimatedAngle);
        ASSERT(propertyTypes[1] == AnimatedEnumeration);
    } else if (propertyTypes.size() == 2)
        ASSERT(propertyTypes[0] == propertyTypes[1]);

    return type;
}

void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement* resultElement)
{
    ASSERT(resultElement);
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return;

    ASSERT(m_animatedPropertyType == determineAnimatedPropertyType(targetElement));

    ASSERT(percentage >= 0 && percentage <= 1);
    ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGNames::animateTransformTag));
    ASSERT(m_animatedPropertyType != AnimatedUnknown);
    ASSERT(m_animator);
    ASSERT(m_animator->type() == m_animatedPropertyType);
    ASSERT(m_fromType);
    ASSERT(m_fromType->type() == m_animatedPropertyType);
    ASSERT(m_toType);

    ASSERT(resultElement->hasTagName(SVGNames::animateTag)
        || resultElement->hasTagName(SVGNames::animateColorTag)
        || resultElement->hasTagName(SVGNames::animateTransformTag)
        || resultElement->hasTagName(SVGNames::setTag));

    SVGAnimateElement* resultAnimationElement = static_cast<SVGAnimateElement*>(resultElement);
    ASSERT(resultAnimationElement->m_animatedType);
    ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyType);

    if (hasTagName(SVGNames::setTag))
        percentage = 1;

    if (calcMode() == CalcModeDiscrete)
        percentage = percentage < 0.5 ? 0 : 1;

    // Target element might have changed.
    m_animator->setContextElement(targetElement);

    // Be sure to detach list wrappers before we modfiy their underlying value. If we'd do
    // if after calculateAnimatedValue() ran the cached pointers in the list propery tear
    // offs would point nowhere, and we couldn't create copies of those values anymore,
    // while detaching. This is covered by assertions, moving this down would fire them.
    if (!m_animatedProperties.isEmpty())
        m_animator->animValWillChange(m_animatedProperties);

    // Values-animation accumulates using the last values entry corresponding to the end of duration time.
    SVGAnimatedType* toAtEndOfDurationType = m_toAtEndOfDurationType ? m_toAtEndOfDurationType.get() : m_toType.get();
    m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromType.get(), m_toType.get(), toAtEndOfDurationType, resultAnimationElement->m_animatedType.get());
}

bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
{
    if (toAtEndOfDurationString.isEmpty())
        return false;
    m_toAtEndOfDurationType = ensureAnimator()->constructFromString(toAtEndOfDurationString);
    return true;
}

bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
{
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return false;

    determinePropertyValueTypes(fromString, toString);
    ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString, toString);
    ASSERT(m_animatedPropertyType == m_animator->type());
    return true;
}

bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
{
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return false;

    if (animationMode() == ByAnimation && !isAdditive())
        return false;

    ASSERT(!hasTagName(SVGNames::setTag));

    determinePropertyValueTypes(fromString, byString);
    ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString, byString);
    ASSERT(m_animatedPropertyType == m_animator->type());
    return true;
}

#ifndef NDEBUG
static inline bool propertyTypesAreConsistent(AnimatedPropertyType expectedPropertyType, const SVGElementAnimatedPropertyList& animatedTypes)
{
    SVGElementAnimatedPropertyList::const_iterator end = animatedTypes.end();
    for (SVGElementAnimatedPropertyList::const_iterator it = animatedTypes.begin(); it != end; ++it) {
        for (size_t i = 0; i < it->properties.size(); ++i) {
            if (expectedPropertyType != it->properties[i]->animatedPropertyType()) {
                // This is the only allowed inconsistency. SVGAnimatedAngleAnimator handles both SVGAnimatedAngle & SVGAnimatedEnumeration for markers orient attribute.
                if (expectedPropertyType == AnimatedAngle && it->properties[i]->animatedPropertyType() == AnimatedEnumeration)
                    return true;
                return false;
            }
        }
    }

    return true;
}
#endif

void SVGAnimateElement::resetAnimatedType()
{
    SVGAnimatedTypeAnimator* animator = ensureAnimator();
    ASSERT(m_animatedPropertyType == animator->type());

    SVGElement* targetElement = this->targetElement();
    const QualifiedName& attributeName = this->attributeName();
    ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
    if (shouldApply == ApplyXMLAnimation) {
        // SVG DOM animVal animation code-path.
        m_animatedProperties = animator->findAnimatedPropertiesForAttributeName(targetElement, attributeName);
        ASSERT(!m_animatedProperties.isEmpty());

        ASSERT(propertyTypesAreConsistent(m_animatedPropertyType, m_animatedProperties));
        if (!m_animatedType)
            m_animatedType = animator->startAnimValAnimation(m_animatedProperties);
        else {
            animator->resetAnimValToBaseVal(m_animatedProperties, m_animatedType.get());
            animator->animValDidChange(m_animatedProperties);
        }
        return;
    }

    // CSS properties animation code-path.
    ASSERT(m_animatedProperties.isEmpty());
    String baseValue;

    if (shouldApply == ApplyCSSAnimation) {
        ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
        computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
    }

    if (!m_animatedType)
        m_animatedType = animator->constructFromString(baseValue);
    else
        m_animatedType->setValueAsString(attributeName, baseValue);
}

static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value)
{
    ASSERT(!targetElement->m_deletionHasBegun);

    MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
    if (!propertySet->setProperty(id, value, false, 0))
        return;

    targetElement->setNeedsStyleRecalc(SyntheticStyleChange);
}

static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id)
{
    ASSERT(!targetElement->m_deletionHasBegun);
    targetElement->ensureAnimatedSMILStyleProperties()->removeProperty(id);
    targetElement->setNeedsStyleRecalc(SyntheticStyleChange);
}

static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString)
{
    ASSERT(targetElement);
    if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
        return;

    CSSPropertyID id = cssPropertyID(attributeName.localName());

    SVGElementInstance::InstanceUpdateBlocker blocker(targetElement);
    applyCSSPropertyToTarget(targetElement, id, valueAsString);

    // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
            applyCSSPropertyToTarget(shadowTreeElement, id, valueAsString);
    }
}

static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName)
{
    ASSERT(targetElement);
    if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
        return;

    CSSPropertyID id = cssPropertyID(attributeName.localName());

    SVGElementInstance::InstanceUpdateBlocker blocker(targetElement);
    removeCSSPropertyFromTarget(targetElement, id);

    // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
            removeCSSPropertyFromTarget(shadowTreeElement, id);
    }
}

static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
{
    ASSERT(!targetElement->m_deletionHasBegun);
    targetElement->svgAttributeChanged(attributeName);
}

static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
{
    ASSERT(targetElement);
    if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
        return;

    SVGElementInstance::InstanceUpdateBlocker blocker(targetElement);
    notifyTargetAboutAnimValChange(targetElement, attributeName);

    // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
            notifyTargetAboutAnimValChange(shadowTreeElement, attributeName);
    }
}

void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement)
{
    if (!m_animatedType)
        return;

    if (!targetElement) {
        m_animatedType.clear();
        return;
    }

    if (m_animatedProperties.isEmpty()) {
        // CSS properties animation code-path.
        removeCSSPropertyFromTargetAndInstances(targetElement, attributeName());
        m_animatedType.clear();
        return;
    }

    // SVG DOM animVal animation code-path.
    if (m_animator) {
        m_animator->stopAnimValAnimation(m_animatedProperties);
        notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName());
    }

    m_animatedProperties.clear();
    m_animatedType.clear();
}

void SVGAnimateElement::applyResultsToTarget()
{
    ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGNames::animateTransformTag));
    ASSERT(m_animatedPropertyType != AnimatedUnknown);
    ASSERT(m_animator);

    // Early exit if our animated type got destructed by a previous endedActiveInterval().
    if (!m_animatedType)
        return;

    if (m_animatedProperties.isEmpty()) {
        // CSS properties animation code-path.
        // Convert the result of the animation to a String and apply it as CSS property on the target & all instances.
        applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m_animatedType->valueAsString());
        return;
    }

    // SVG DOM animVal animation code-path.
    // At this point the SVG DOM values are already changed, unlike for CSS.
    // We only have to trigger update notifications here.
    m_animator->animValDidChange(m_animatedProperties);
    notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName());
}

bool SVGAnimateElement::isAdditive() const
{
    if (animationMode() == ByAnimation || animationMode() == FromByAnimation) {
        // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
        switch (m_animatedPropertyType) {
        case AnimatedBoolean:
        case AnimatedEnumeration:
        case AnimatedPreserveAspectRatio:
        case AnimatedString:
        case AnimatedUnknown:
            return false;
        default:
            break;
        }
    }

    return SVGAnimationElement::isAdditive();
}

float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
{
    // FIXME: A return value of float is not enough to support paced animations on lists.
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return -1;

    return ensureAnimator()->calculateDistance(fromString, toString);
}

void SVGAnimateElement::setTargetElement(SVGElement* target)
{
    SVGAnimationElement::setTargetElement(target);
    resetAnimatedPropertyType();
}

void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName)
{
    SVGAnimationElement::setAttributeName(attributeName);
    resetAnimatedPropertyType();
}

void SVGAnimateElement::resetAnimatedPropertyType()
{
    ASSERT(!m_animatedType);
    m_fromType.clear();
    m_toType.clear();
    m_toAtEndOfDurationType.clear();
    m_animator.clear();
    m_animatedPropertyType = targetElement() ? determineAnimatedPropertyType(targetElement()) : AnimatedString;
}

SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
{
    if (!m_animator)
        m_animator = SVGAnimatorFactory::create(this, targetElement(), m_animatedPropertyType);
    ASSERT(m_animatedPropertyType == m_animator->type());
    return m_animator.get();
}

}
<commit_msg>SVGAnimateElement should use InlineStyleChange<commit_after>/*
 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
 * Copyright (C) 2008 Apple Inc. All rights reserved.
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#include "core/svg/SVGAnimateElement.h"

#include "CSSPropertyNames.h"
#include "SVGNames.h"
#include "core/css/CSSParser.h"
#include "core/css/StylePropertySet.h"
#include "core/dom/QualifiedName.h"
#include "core/svg/SVGAnimatorFactory.h"

namespace WebCore {

SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document* document)
    : SVGAnimationElement(tagName, document)
    , m_animatedPropertyType(AnimatedString)
{
    ASSERT(hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::setTag) || hasTagName(SVGNames::animateColorTag) || hasTagName(SVGNames::animateTransformTag));
    ScriptWrappable::init(this);
}

PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(const QualifiedName& tagName, Document* document)
{
    return adoptRef(new SVGAnimateElement(tagName, document));
}

SVGAnimateElement::~SVGAnimateElement()
{
}

bool SVGAnimateElement::hasValidAttributeType()
{
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return false;

    return m_animatedPropertyType != AnimatedUnknown && !hasInvalidCSSAttributeType();
}

AnimatedPropertyType SVGAnimateElement::determineAnimatedPropertyType(SVGElement* targetElement) const
{
    ASSERT(targetElement);

    Vector<AnimatedPropertyType> propertyTypes;
    targetElement->animatedPropertyTypeForAttribute(attributeName(), propertyTypes);
    if (propertyTypes.isEmpty())
        return AnimatedUnknown;

    ASSERT(propertyTypes.size() <= 2);
    AnimatedPropertyType type = propertyTypes[0];
    if (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor)
        return AnimatedUnknown;

    // Animations of transform lists are not allowed for <animate> or <set>
    // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
    if (type == AnimatedTransformList && !hasTagName(SVGNames::animateTransformTag))
        return AnimatedUnknown;

    // Fortunately there's just one special case needed here: SVGMarkerElements orientAttr, which
    // corresponds to SVGAnimatedAngle orientAngle and SVGAnimatedEnumeration orientType. We have to
    // figure out whose value to change here.
    if (targetElement->hasTagName(SVGNames::markerTag) && type == AnimatedAngle) {
        ASSERT(propertyTypes.size() == 2);
        ASSERT(propertyTypes[0] == AnimatedAngle);
        ASSERT(propertyTypes[1] == AnimatedEnumeration);
    } else if (propertyTypes.size() == 2)
        ASSERT(propertyTypes[0] == propertyTypes[1]);

    return type;
}

void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement* resultElement)
{
    ASSERT(resultElement);
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return;

    ASSERT(m_animatedPropertyType == determineAnimatedPropertyType(targetElement));

    ASSERT(percentage >= 0 && percentage <= 1);
    ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGNames::animateTransformTag));
    ASSERT(m_animatedPropertyType != AnimatedUnknown);
    ASSERT(m_animator);
    ASSERT(m_animator->type() == m_animatedPropertyType);
    ASSERT(m_fromType);
    ASSERT(m_fromType->type() == m_animatedPropertyType);
    ASSERT(m_toType);

    ASSERT(resultElement->hasTagName(SVGNames::animateTag)
        || resultElement->hasTagName(SVGNames::animateColorTag)
        || resultElement->hasTagName(SVGNames::animateTransformTag)
        || resultElement->hasTagName(SVGNames::setTag));

    SVGAnimateElement* resultAnimationElement = static_cast<SVGAnimateElement*>(resultElement);
    ASSERT(resultAnimationElement->m_animatedType);
    ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyType);

    if (hasTagName(SVGNames::setTag))
        percentage = 1;

    if (calcMode() == CalcModeDiscrete)
        percentage = percentage < 0.5 ? 0 : 1;

    // Target element might have changed.
    m_animator->setContextElement(targetElement);

    // Be sure to detach list wrappers before we modfiy their underlying value. If we'd do
    // if after calculateAnimatedValue() ran the cached pointers in the list propery tear
    // offs would point nowhere, and we couldn't create copies of those values anymore,
    // while detaching. This is covered by assertions, moving this down would fire them.
    if (!m_animatedProperties.isEmpty())
        m_animator->animValWillChange(m_animatedProperties);

    // Values-animation accumulates using the last values entry corresponding to the end of duration time.
    SVGAnimatedType* toAtEndOfDurationType = m_toAtEndOfDurationType ? m_toAtEndOfDurationType.get() : m_toType.get();
    m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromType.get(), m_toType.get(), toAtEndOfDurationType, resultAnimationElement->m_animatedType.get());
}

bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
{
    if (toAtEndOfDurationString.isEmpty())
        return false;
    m_toAtEndOfDurationType = ensureAnimator()->constructFromString(toAtEndOfDurationString);
    return true;
}

bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
{
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return false;

    determinePropertyValueTypes(fromString, toString);
    ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString, toString);
    ASSERT(m_animatedPropertyType == m_animator->type());
    return true;
}

bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
{
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return false;

    if (animationMode() == ByAnimation && !isAdditive())
        return false;

    ASSERT(!hasTagName(SVGNames::setTag));

    determinePropertyValueTypes(fromString, byString);
    ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString, byString);
    ASSERT(m_animatedPropertyType == m_animator->type());
    return true;
}

#ifndef NDEBUG
static inline bool propertyTypesAreConsistent(AnimatedPropertyType expectedPropertyType, const SVGElementAnimatedPropertyList& animatedTypes)
{
    SVGElementAnimatedPropertyList::const_iterator end = animatedTypes.end();
    for (SVGElementAnimatedPropertyList::const_iterator it = animatedTypes.begin(); it != end; ++it) {
        for (size_t i = 0; i < it->properties.size(); ++i) {
            if (expectedPropertyType != it->properties[i]->animatedPropertyType()) {
                // This is the only allowed inconsistency. SVGAnimatedAngleAnimator handles both SVGAnimatedAngle & SVGAnimatedEnumeration for markers orient attribute.
                if (expectedPropertyType == AnimatedAngle && it->properties[i]->animatedPropertyType() == AnimatedEnumeration)
                    return true;
                return false;
            }
        }
    }

    return true;
}
#endif

void SVGAnimateElement::resetAnimatedType()
{
    SVGAnimatedTypeAnimator* animator = ensureAnimator();
    ASSERT(m_animatedPropertyType == animator->type());

    SVGElement* targetElement = this->targetElement();
    const QualifiedName& attributeName = this->attributeName();
    ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
    if (shouldApply == ApplyXMLAnimation) {
        // SVG DOM animVal animation code-path.
        m_animatedProperties = animator->findAnimatedPropertiesForAttributeName(targetElement, attributeName);
        ASSERT(!m_animatedProperties.isEmpty());

        ASSERT(propertyTypesAreConsistent(m_animatedPropertyType, m_animatedProperties));
        if (!m_animatedType)
            m_animatedType = animator->startAnimValAnimation(m_animatedProperties);
        else {
            animator->resetAnimValToBaseVal(m_animatedProperties, m_animatedType.get());
            animator->animValDidChange(m_animatedProperties);
        }
        return;
    }

    // CSS properties animation code-path.
    ASSERT(m_animatedProperties.isEmpty());
    String baseValue;

    if (shouldApply == ApplyCSSAnimation) {
        ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
        computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
    }

    if (!m_animatedType)
        m_animatedType = animator->constructFromString(baseValue);
    else
        m_animatedType->setValueAsString(attributeName, baseValue);
}

static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value)
{
    ASSERT(!targetElement->m_deletionHasBegun);

    MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
    if (!propertySet->setProperty(id, value, false, 0))
        return;

    targetElement->setNeedsStyleRecalc(InlineStyleChange);
}

static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id)
{
    ASSERT(!targetElement->m_deletionHasBegun);
    targetElement->ensureAnimatedSMILStyleProperties()->removeProperty(id);
    targetElement->setNeedsStyleRecalc(InlineStyleChange);
}

static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString)
{
    ASSERT(targetElement);
    if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
        return;

    CSSPropertyID id = cssPropertyID(attributeName.localName());

    SVGElementInstance::InstanceUpdateBlocker blocker(targetElement);
    applyCSSPropertyToTarget(targetElement, id, valueAsString);

    // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
            applyCSSPropertyToTarget(shadowTreeElement, id, valueAsString);
    }
}

static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName)
{
    ASSERT(targetElement);
    if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
        return;

    CSSPropertyID id = cssPropertyID(attributeName.localName());

    SVGElementInstance::InstanceUpdateBlocker blocker(targetElement);
    removeCSSPropertyFromTarget(targetElement, id);

    // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
            removeCSSPropertyFromTarget(shadowTreeElement, id);
    }
}

static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
{
    ASSERT(!targetElement->m_deletionHasBegun);
    targetElement->svgAttributeChanged(attributeName);
}

static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
{
    ASSERT(targetElement);
    if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
        return;

    SVGElementInstance::InstanceUpdateBlocker blocker(targetElement);
    notifyTargetAboutAnimValChange(targetElement, attributeName);

    // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
            notifyTargetAboutAnimValChange(shadowTreeElement, attributeName);
    }
}

void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement)
{
    if (!m_animatedType)
        return;

    if (!targetElement) {
        m_animatedType.clear();
        return;
    }

    if (m_animatedProperties.isEmpty()) {
        // CSS properties animation code-path.
        removeCSSPropertyFromTargetAndInstances(targetElement, attributeName());
        m_animatedType.clear();
        return;
    }

    // SVG DOM animVal animation code-path.
    if (m_animator) {
        m_animator->stopAnimValAnimation(m_animatedProperties);
        notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName());
    }

    m_animatedProperties.clear();
    m_animatedType.clear();
}

void SVGAnimateElement::applyResultsToTarget()
{
    ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGNames::animateTransformTag));
    ASSERT(m_animatedPropertyType != AnimatedUnknown);
    ASSERT(m_animator);

    // Early exit if our animated type got destructed by a previous endedActiveInterval().
    if (!m_animatedType)
        return;

    if (m_animatedProperties.isEmpty()) {
        // CSS properties animation code-path.
        // Convert the result of the animation to a String and apply it as CSS property on the target & all instances.
        applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m_animatedType->valueAsString());
        return;
    }

    // SVG DOM animVal animation code-path.
    // At this point the SVG DOM values are already changed, unlike for CSS.
    // We only have to trigger update notifications here.
    m_animator->animValDidChange(m_animatedProperties);
    notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName());
}

bool SVGAnimateElement::isAdditive() const
{
    if (animationMode() == ByAnimation || animationMode() == FromByAnimation) {
        // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
        switch (m_animatedPropertyType) {
        case AnimatedBoolean:
        case AnimatedEnumeration:
        case AnimatedPreserveAspectRatio:
        case AnimatedString:
        case AnimatedUnknown:
            return false;
        default:
            break;
        }
    }

    return SVGAnimationElement::isAdditive();
}

float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
{
    // FIXME: A return value of float is not enough to support paced animations on lists.
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return -1;

    return ensureAnimator()->calculateDistance(fromString, toString);
}

void SVGAnimateElement::setTargetElement(SVGElement* target)
{
    SVGAnimationElement::setTargetElement(target);
    resetAnimatedPropertyType();
}

void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName)
{
    SVGAnimationElement::setAttributeName(attributeName);
    resetAnimatedPropertyType();
}

void SVGAnimateElement::resetAnimatedPropertyType()
{
    ASSERT(!m_animatedType);
    m_fromType.clear();
    m_toType.clear();
    m_toAtEndOfDurationType.clear();
    m_animator.clear();
    m_animatedPropertyType = targetElement() ? determineAnimatedPropertyType(targetElement()) : AnimatedString;
}

SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
{
    if (!m_animator)
        m_animator = SVGAnimatorFactory::create(this, targetElement(), m_animatedPropertyType);
    ASSERT(m_animatedPropertyType == m_animator->type());
    return m_animator.get();
}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkRRect.h"

///////////////////////////////////////////////////////////////////////////////

void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
    if (rect.isEmpty()) {
        this->setEmpty();
        return;
    }

    if (xRad <= 0 || yRad <= 0) {
        // all corners are square in this case
        this->setRect(rect);
        return;
    }

    if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
        SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad),
                                     SkScalarDiv(rect.height(), yRad + yRad));
        SkASSERT(scale < SK_Scalar1);
        xRad = SkScalarMul(xRad, scale);
        yRad = SkScalarMul(yRad, scale);
    }

    fRect = rect;
    for (int i = 0; i < 4; ++i) {
        fRadii[i].set(xRad, yRad);
    }
    fType = kSimple_Type;
    if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
        fType = kOval_Type;
        // TODO: try asserting they are already W/2 & H/2 already
        xRad = SkScalarHalf(fRect.width());
        yRad = SkScalarHalf(fRect.height());
    }

    SkDEBUGCODE(this->validate();)
}

void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
    if (rect.isEmpty()) {
        this->setEmpty();
        return;
    }

    fRect = rect;
    memcpy(fRadii, radii, sizeof(fRadii));

    bool allCornersSquare = true;

    // Clamp negative radii to zero
    for (int i = 0; i < 4; ++i) {
        if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
            // In this case we are being a little fast & loose. Since one of
            // the radii is 0 the corner is square. However, the other radii
            // could still be non-zero and play in the global scale factor
            // computation.
            fRadii[i].fX = 0;
            fRadii[i].fY = 0;
        } else {
            allCornersSquare = false;
        }
    }

    if (allCornersSquare) {
        this->setRect(rect);
        return;
    }

    // Proportionally scale down all radii to fit. Find the minimum ratio
    // of a side and the radii on that side (for all four sides) and use
    // that to scale down _all_ the radii. This algorithm is from the
    // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
    // Curves:
    // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
    //   Si is the sum of the two corresponding radii of the corners on side i,
    //   and Ltop = Lbottom = the width of the box,
    //   and Lleft = Lright = the height of the box.
    // If f < 1, then all corner radii are reduced by multiplying them by f."
    SkScalar scale = SK_Scalar1;

    if (fRadii[0].fX + fRadii[1].fX > rect.width()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX));
    }
    if (fRadii[1].fY + fRadii[2].fY > rect.height()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY));
    }
    if (fRadii[2].fX + fRadii[3].fX > rect.width()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX));
    }
    if (fRadii[3].fY + fRadii[0].fY > rect.height()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY));
    }

    if (scale < SK_Scalar1) {
        for (int i = 0; i < 4; ++i) {
            fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale);
            fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale);
        }
    }

    // At this point we're either oval, simple, or complex (not empty or rect)
    // but we lazily resolve the type to avoid the work if the information
    // isn't required.
    fType = (SkRRect::Type) kUnknown_Type;

    SkDEBUGCODE(this->validate();)
}

bool SkRRect::contains(SkScalar x, SkScalar y) const {
    SkDEBUGCODE(this->validate();)

    if (kEmpty_Type == this->type()) {
        return false;
    }

    if (!fRect.contains(x, y)) {
        return false;
    }

    if (kRect_Type == this->type()) {
        // the 'fRect' test above was sufficient
        return true;
    }

    // We know the point is inside the RR's bounds. The only way it can
    // be out is if it outside one of the corners
    SkPoint canonicalPt; // (x,y) translated to one of the quadrants
    int index;

    if (kOval_Type == this->type()) {
        canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
        index = kUpperLeft_Corner;  // any corner will do in this case
    } else {
        if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
            y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
            // UL corner
            index = kUpperLeft_Corner;
            canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
                            y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
        } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
                   y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
            // LL corner
            index = kLowerLeft_Corner;
            canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
                            y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
        } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
                   y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
            // UR corner
            index = kUpperRight_Corner;
            canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
                            y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
        } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
                   y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
            // LR corner
            index = kLowerRight_Corner;
            canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
                            y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
        } else {
            // not in any of the corners
            return true;
        }
    }

    // A point is in an ellipse (in standard position) if:
    //      x^2     y^2
    //     ----- + ----- <= 1
    //      a^2     b^2
    SkScalar dist =  SkScalarDiv(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fX)) +
                     SkScalarDiv(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fY));
    return dist <= SK_Scalar1;
}

// There is a simplified version of this method in setRectXY
void SkRRect::computeType() const {
    SkDEBUGCODE(this->validate();)

    if (fRect.isEmpty()) {
        fType = kEmpty_Type;
        return;
    }

    bool allRadiiEqual = true; // are all x radii equal and all y radii?
    bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;

    for (int i = 1; i < 4; ++i) {
        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
            // if either radius is zero the corner is square so both have to
            // be non-zero to have a rounded corner
            allCornersSquare = false;
        }
        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
            allRadiiEqual = false;
        }
    }

    if (allCornersSquare) {
        fType = kRect_Type;
        return;
    }

    if (allRadiiEqual) {
        if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
            fRadii[0].fY >= SkScalarHalf(fRect.height())) {
            fType = kOval_Type;
        } else {
            fType = kSimple_Type;
        }
        return;
    }

    fType = kComplex_Type;
}

#ifdef SK_DEBUG
void SkRRect::validate() const {
    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
    bool allRadiiSame = true;

    for (int i = 1; i < 4; ++i) {
        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
            allRadiiZero = false;
        }

        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
            allRadiiSame = false;
        }

        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
            allCornersSquare = false;
        }
    }

    switch (fType) {
        case kEmpty_Type:
            SkASSERT(fRect.isEmpty());
            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);

            SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop &&
                     0 == fRect.fRight && 0 == fRect.fBottom);
            break;
        case kRect_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
            break;
        case kOval_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);

            for (int i = 0; i < 4; ++i) {
                SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
                SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
            }
            break;
        case kSimple_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
            break;
        case kComplex_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
            break;
        case kUnknown_Type:
            // no limits on this
            break;
    }
}
#endif // SK_DEBUG

///////////////////////////////////////////////////////////////////////////////
<commit_msg>Fix for roll-blocking compiler error in SkRRect.cpp<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkRRect.h"

///////////////////////////////////////////////////////////////////////////////

void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
    if (rect.isEmpty()) {
        this->setEmpty();
        return;
    }

    if (xRad <= 0 || yRad <= 0) {
        // all corners are square in this case
        this->setRect(rect);
        return;
    }

    if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
        SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad),
                                     SkScalarDiv(rect.height(), yRad + yRad));
        SkASSERT(scale < SK_Scalar1);
        xRad = SkScalarMul(xRad, scale);
        yRad = SkScalarMul(yRad, scale);
    }

    fRect = rect;
    for (int i = 0; i < 4; ++i) {
        fRadii[i].set(xRad, yRad);
    }
    fType = kSimple_Type;
    if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
        fType = kOval_Type;
        // TODO: try asserting they are already W/2 & H/2 already
        xRad = SkScalarHalf(fRect.width());
        yRad = SkScalarHalf(fRect.height());
    }

    SkDEBUGCODE(this->validate();)
}

void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
    if (rect.isEmpty()) {
        this->setEmpty();
        return;
    }

    fRect = rect;
    memcpy(fRadii, radii, sizeof(fRadii));

    bool allCornersSquare = true;

    // Clamp negative radii to zero
    for (int i = 0; i < 4; ++i) {
        if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
            // In this case we are being a little fast & loose. Since one of
            // the radii is 0 the corner is square. However, the other radii
            // could still be non-zero and play in the global scale factor
            // computation.
            fRadii[i].fX = 0;
            fRadii[i].fY = 0;
        } else {
            allCornersSquare = false;
        }
    }

    if (allCornersSquare) {
        this->setRect(rect);
        return;
    }

    // Proportionally scale down all radii to fit. Find the minimum ratio
    // of a side and the radii on that side (for all four sides) and use
    // that to scale down _all_ the radii. This algorithm is from the
    // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
    // Curves:
    // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
    //   Si is the sum of the two corresponding radii of the corners on side i,
    //   and Ltop = Lbottom = the width of the box,
    //   and Lleft = Lright = the height of the box.
    // If f < 1, then all corner radii are reduced by multiplying them by f."
    SkScalar scale = SK_Scalar1;

    if (fRadii[0].fX + fRadii[1].fX > rect.width()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX));
    }
    if (fRadii[1].fY + fRadii[2].fY > rect.height()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY));
    }
    if (fRadii[2].fX + fRadii[3].fX > rect.width()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX));
    }
    if (fRadii[3].fY + fRadii[0].fY > rect.height()) {
        scale = SkMinScalar(scale,
                            SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY));
    }

    if (scale < SK_Scalar1) {
        for (int i = 0; i < 4; ++i) {
            fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale);
            fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale);
        }
    }

    // At this point we're either oval, simple, or complex (not empty or rect)
    // but we lazily resolve the type to avoid the work if the information
    // isn't required.
    fType = (SkRRect::Type) kUnknown_Type;

    SkDEBUGCODE(this->validate();)
}

bool SkRRect::contains(SkScalar x, SkScalar y) const {
    SkDEBUGCODE(this->validate();)

    if (kEmpty_Type == this->type()) {
        return false;
    }

    if (!fRect.contains(x, y)) {
        return false;
    }

    if (kRect_Type == this->type()) {
        // the 'fRect' test above was sufficient
        return true;
    }

    // We know the point is inside the RR's bounds. The only way it can
    // be out is if it outside one of the corners
    SkPoint canonicalPt; // (x,y) translated to one of the quadrants
    int index;

    if (kOval_Type == this->type()) {
        canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
        index = kUpperLeft_Corner;  // any corner will do in this case
    } else {
        if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
            y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
            // UL corner
            index = kUpperLeft_Corner;
            canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
                            y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
        } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
                   y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
            // LL corner
            index = kLowerLeft_Corner;
            canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
                            y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
        } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
                   y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
            // UR corner
            index = kUpperRight_Corner;
            canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
                            y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
        } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
                   y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
            // LR corner
            index = kLowerRight_Corner;
            canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
                            y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
        } else {
            // not in any of the corners
            return true;
        }
    }

    // A point is in an ellipse (in standard position) if:
    //      x^2     y^2
    //     ----- + ----- <= 1
    //      a^2     b^2
    SkScalar dist =  SkScalarDiv(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fX)) +
                     SkScalarDiv(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fY));
    return dist <= SK_Scalar1;
}

// There is a simplified version of this method in setRectXY
void SkRRect::computeType() const {
    SkDEBUGCODE(this->validate();)

    if (fRect.isEmpty()) {
        fType = kEmpty_Type;
        return;
    }

    bool allRadiiEqual = true; // are all x radii equal and all y radii?
    bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;

    for (int i = 1; i < 4; ++i) {
        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
            // if either radius is zero the corner is square so both have to
            // be non-zero to have a rounded corner
            allCornersSquare = false;
        }
        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
            allRadiiEqual = false;
        }
    }

    if (allCornersSquare) {
        fType = kRect_Type;
        return;
    }

    if (allRadiiEqual) {
        if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
            fRadii[0].fY >= SkScalarHalf(fRect.height())) {
            fType = kOval_Type;
        } else {
            fType = kSimple_Type;
        }
        return;
    }

    fType = kComplex_Type;
}

#ifdef SK_DEBUG
void SkRRect::validate() const {
    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
    bool allRadiiSame = true;

    for (int i = 1; i < 4; ++i) {
        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
            allRadiiZero = false;
        }

        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
            allRadiiSame = false;
        }

        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
            allCornersSquare = false;
        }
    }

    switch (fType) {
        case kEmpty_Type:
            SkASSERT(fRect.isEmpty());
            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);

            SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop &&
                     0 == fRect.fRight && 0 == fRect.fBottom);
            break;
        case kRect_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
            break;
        case kOval_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);

            for (int i = 0; i < 4; ++i) {
                SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
                SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
            }
            break;
        case kSimple_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
            break;
        case kComplex_Type:
            SkASSERT(!fRect.isEmpty());
            SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
            break;
#if 0
        // error: case value not in enumerated type 'SkRRect::Type
        case kUnknown_Type:
            // no limits on this
            break;
#endif
    }
}
#endif // SK_DEBUG

///////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>#include <cppunit/extensions/HelperMacros.h>

#include "comparatortest.h"

#include "comparator.h"
#include "filesystem.h"
#include "file.h"

using namespace findd;
using filesystem::real_path;

CPPUNIT_TEST_SUITE_REGISTRATION( ComparatorTest );

void ComparatorTest::setUp () {
  _comparator = new Comparator();
}

void ComparatorTest::tearDown () {
  delete _comparator;
}

void ComparatorTest::testIsEnable () {
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == false );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == false );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == false );

  _comparator->enable(NAME);
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == false );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == false );

  _comparator->enable(SIZE);
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == false );

  _comparator->enable(CONTENT);
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == true );
}

void ComparatorTest::testComparaisionByName () {
  _comparator->enable(NAME);
  File f1("/foo/bar", 0), f2("/foo/bar", 1), f3("/bar/foo", 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
}

void ComparatorTest::testComparaisionBySize () {
  _comparator->enable(SIZE);
  File f1("/foo/bar", 1), f2("/bar/foo", 1), f3("/bar/foo", 0);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
}

void ComparatorTest::testComparaisionByContent () {
  _comparator->enable(CONTENT);
  File f1(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/test_sub_dir/file_c.txt"), 1);
  File f2(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/file_a.txt"), 1);
  File f3(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/file_b.txt"), 2);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
}

void ComparatorTest::testComparaisionByNameAndSize () {
  _comparator->enable(NAME);
  _comparator->enable(SIZE);
  File f1("/foo/bar", 1), f2("/foo/bar", 1), f3("/foo/bar", 0), f4("/bar/foo", 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
  CPPUNIT_ASSERT( (*_comparator)(f1, f4) == false );
}

void ComparatorTest::testComparaisionByNameAndContent () {
  _comparator->enable(NAME);
  _comparator->enable(CONTENT);
  File f1(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/file_a.txt"), 1);
  File f2(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_name/file_a.txt"), 1);
  File f3(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/file_b.txt"), 2);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
  CPPUNIT_ASSERT( (*_comparator)(f1, f4) == false );
}

void ComparatorTest::testComparaisionBySizeAndContent () {

}

void ComparatorTest::testComparaisionByNameAndSizeAndContent () {

}
<commit_msg>test comparator finished<commit_after>#include <cppunit/extensions/HelperMacros.h>

#include "comparatortest.h"

#include "comparator.h"
#include "filesystem.h"
#include "file.h"

using namespace findd;
using filesystem::real_path;

CPPUNIT_TEST_SUITE_REGISTRATION( ComparatorTest );

void ComparatorTest::setUp () {
  _comparator = new Comparator();
}

void ComparatorTest::tearDown () {
  delete _comparator;
}

void ComparatorTest::testIsEnable () {
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == false );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == false );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == false );

  _comparator->enable(NAME);
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == false );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == false );

  _comparator->enable(SIZE);
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == false );

  _comparator->enable(CONTENT);
  CPPUNIT_ASSERT( _comparator->is_enabled(NAME) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(SIZE) == true );
  CPPUNIT_ASSERT( _comparator->is_enabled(CONTENT) == true );
}

void ComparatorTest::testComparaisionByName () {
  _comparator->enable(NAME);
  File f1("/foo/bar", 0), f2("/foo/bar", 1), f3("/bar/foo", 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
}

void ComparatorTest::testComparaisionBySize () {
  _comparator->enable(SIZE);
  File f1("/foo/bar", 1), f2("/bar/foo", 1), f3("/bar/foo", 0);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
}

void ComparatorTest::testComparaisionByContent () {
  _comparator->enable(CONTENT);
  File f1(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/test_sub_dir/file_c.txt"), 1);
  File f2(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/file_a.txt"), 1);
  File f3(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_content/file_b.txt"), 2);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
}

void ComparatorTest::testComparaisionByNameAndSize () {
  _comparator->enable(NAME);
  _comparator->enable(SIZE);
  File f1("/foo/bar", 1), f2("/foo/bar", 1), f3("/foo/bar", 0), f4("/bar/foo", 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
  CPPUNIT_ASSERT( (*_comparator)(f1, f3) == false );
  CPPUNIT_ASSERT( (*_comparator)(f1, f4) == false );
}

void ComparatorTest::testComparaisionByNameAndContent () {
  _comparator->enable(NAME);
  _comparator->enable(CONTENT);
  File f1(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_name_and_content/file_a.txt"), 1);
  File f2(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_name_and_content/file_a.txt"), 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
}

void ComparatorTest::testComparaisionBySizeAndContent () {
  _comparator->enable(SIZE);
  _comparator->enable(CONTENT);
  File f1(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_size_and_content/file_a.txt"), 1);
  File f2(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_size_and_content/file_a.txt"), 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
}

void ComparatorTest::testComparaisionByNameAndSizeAndContent () {
  _comparator->enable(SIZE);
  _comparator->enable(CONTENT);
  File f1(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_name_and_size_and_content/file_a.txt"), 1);
  File f2(real_path("unittest/fixtures/test_not_empty_dir/test_files_with_same_name_and_size_and_content/file_a.txt"), 1);

  CPPUNIT_ASSERT( (*_comparator)(f1, f2) == true );
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
#ifndef DISABLE_WARNINGS

#include "singa/core/memory.h"
#include "singa/utils/logging.h"
#include "singa/proto/core.pb.h"
#include <iostream>

#ifdef USE_CUDA

namespace singa {
std::pair<size_t, size_t> CnMemPool::GetMemUsage() {
  size_t free, total;
  auto status = cnmemMemGetInfo(&free, &total, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
    << cnmemGetErrorString(status);
  return std::make_pair(free, total);
}
std::pair<size_t, size_t> CnMemPool::GetMemUsage(int id) {
  CHECK_EQ(cudaSetDevice(id), cudaError_t::cudaSuccess);
  size_t free, total;
  auto status = cnmemMemGetInfo(&free, &total, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
    << cnmemGetErrorString(status);
  return std::make_pair(free, total);
}

CnMemPool::CnMemPool(int numDevices, size_t init_size, size_t max_size) {
  for (int i = 0; i < numDevices; i++)
    conf_.add_device(i);
  conf_.set_init_size(init_size);
  conf_.set_max_size(max_size);
}

CnMemPool::CnMemPool(const MemPoolConf &conf) {
  conf_ = conf;
}

void CnMemPool::Init() {
  mtx_.lock();
  if (!initialized_) {
    const size_t kNBytesPerMB = (1u << 10);
    CHECK_GE(conf_.device_size(), 1);
    cnmemDevice_t *settingPtr = new cnmemDevice_t[conf_.device_size()];
    CHECK_GT(conf_.init_size(), 0u);
    int i = 0;
    for (auto device : conf_.device()) {
      settingPtr[i].device = device;
      settingPtr[i].size = conf_.init_size() * kNBytesPerMB;
      settingPtr[i].numStreams = 0;
      settingPtr[i].streams = NULL;
      settingPtr[i].streamSizes = 0;
      i++;
    }
    auto status = cnmemInit(conf_.device_size(), settingPtr, conf_.flag());
    CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
        << " " << cnmemGetErrorString(status);
    delete[] settingPtr;
    initialized_ = true;
  }
  mtx_.unlock();
}

CnMemPool::~CnMemPool() {
  mtx_.lock();
  if (initialized_) {
    cnmemStatus_t status = cnmemFinalize();
    CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
        << " " << cnmemGetErrorString(status);
    initialized_ = false;
  }
  mtx_.unlock();
}

void CnMemPool::Malloc(void **ptr, const size_t size) {
  if (!initialized_)
    Init();
  cnmemStatus_t status = cnmemMalloc(ptr, size, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
      << " " << cnmemGetErrorString(status);
}

void CnMemPool::Free(void *ptr) {
  CHECK(initialized_) << "Cannot free the memory as the pool is not initialzied";
  cnmemStatus_t status = cnmemFree(ptr, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
      << " " << cnmemGetErrorString(status);
}

// ===========================================================================
void CudaMemPool::Malloc(void **ptr, const size_t size) {
  cudaError_t status = cudaMalloc(ptr, size);
  CHECK_EQ(status, cudaError_t::cudaSuccess);
}

void CudaMemPool::Free(void *ptr) {
  cudaError_t status = cudaFree(ptr);
  CHECK_EQ(status, cudaError_t::cudaSuccess);
}
}
#endif

#endif
<commit_msg>SINGA-477 add two memory pool test<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
#ifndef DISABLE_WARNINGS

#include "singa/core/memory.h"
#include "singa/utils/logging.h"
#include "singa/proto/core.pb.h"
#include <iostream>

#ifdef USE_CUDA

namespace singa {
std::pair<size_t, size_t> CnMemPool::GetMemUsage() {
  size_t free, total;
  auto status = cnmemMemGetInfo(&free, &total, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
    << cnmemGetErrorString(status);
  return std::make_pair(free, total);
}
std::pair<size_t, size_t> CnMemPool::GetMemUsage(int id) {
  CHECK_EQ(cudaSetDevice(id), cudaError_t::cudaSuccess);
  size_t free, total;
  auto status = cnmemMemGetInfo(&free, &total, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
    << cnmemGetErrorString(status);
  return std::make_pair(free, total);
}

CnMemPool::CnMemPool(int numDevices, size_t init_size, size_t max_size) {
  for (int i = 0; i < numDevices; i++)
    conf_.add_device(i);
  conf_.set_init_size(init_size);
  conf_.set_max_size(max_size);
}

CnMemPool::CnMemPool(const MemPoolConf &conf) {
  conf_ = conf;
}

void CnMemPool::Init() {
  mtx_.lock();
  if (!initialized_) {
    const size_t kNBytesPerMB = (1u << 20);
    CHECK_GE(conf_.device_size(), 1);
    cnmemDevice_t *settingPtr = new cnmemDevice_t[conf_.device_size()];
    CHECK_GT(conf_.init_size(), 0u);
    int i = 0;
    for (auto device : conf_.device()) {
      settingPtr[i].device = device;
      settingPtr[i].size = conf_.init_size() * kNBytesPerMB;
      settingPtr[i].numStreams = 0;
      settingPtr[i].streams = NULL;
      settingPtr[i].streamSizes = 0;
      i++;
    }
    auto status = cnmemInit(conf_.device_size(), settingPtr, conf_.flag());
    CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
        << " " << cnmemGetErrorString(status);
    delete[] settingPtr;
    initialized_ = true;
  }
  mtx_.unlock();
}

CnMemPool::~CnMemPool() {
  mtx_.lock();
  if (initialized_) {
    cnmemStatus_t status = cnmemFinalize();
    CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
        << " " << cnmemGetErrorString(status);
    initialized_ = false;
  }
  mtx_.unlock();
}

void CnMemPool::Malloc(void **ptr, const size_t size) {
  if (!initialized_)
    Init();
  cnmemStatus_t status = cnmemMalloc(ptr, size, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
      << " " << cnmemGetErrorString(status);
}

void CnMemPool::Free(void *ptr) {
  CHECK(initialized_) << "Cannot free the memory as the pool is not initialzied";
  cnmemStatus_t status = cnmemFree(ptr, NULL);
  CHECK_EQ(status, cnmemStatus_t::CNMEM_STATUS_SUCCESS)
      << " " << cnmemGetErrorString(status);
}

// ===========================================================================
void CudaMemPool::Malloc(void **ptr, const size_t size) {
  cudaError_t status = cudaMalloc(ptr, size);
  CHECK_EQ(status, cudaError_t::cudaSuccess);
}

void CudaMemPool::Free(void *ptr) {
  cudaError_t status = cudaFree(ptr);
  CHECK_EQ(status, cudaError_t::cudaSuccess);
}
}
#endif

#endif
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file implements the necessary hooks for mbedTLS.
 */

#include <assert.h>
#include <stdio.h>

#include <common/code_utils.hpp>
#include <common/encoding.hpp>
#include <common/logging.hpp>
#include <common/timer.hpp>
#include <crypto/sha256.hpp>
#include <meshcop/dtls.hpp>
#include <thread/thread_netif.hpp>

#include <mbedtls/debug.h>

namespace Thread {
namespace MeshCoP {

Dtls::Dtls(ThreadNetif &aNetif):
    mStarted(false),
    mTimer(aNetif.GetIp6().mTimerScheduler, &HandleTimer, this),
    mTimerIntermediate(0),
    mTimerSet(false),
    mNetif(aNetif)
{
}

ThreadError Dtls::Start(bool aClient, ReceiveHandler aReceiveHandler, SendHandler aSendHandler, void *aContext)
{
    static const int ciphersuites[2] = {0xC0FF, 0}; // EC-JPAKE cipher suite
    int rval;

    mReceiveHandler = aReceiveHandler;
    mSendHandler = aSendHandler;
    mContext = aContext;
    mClient = aClient;

    mbedtls_ssl_init(&mSsl);
    mbedtls_ssl_config_init(&mConf);
    mbedtls_ctr_drbg_init(&mCtrDrbg);
    mbedtls_entropy_init(&mEntropy);

    mbedtls_debug_set_threshold(4);

    // XXX: should set personalization data to hardware address
    rval = mbedtls_ctr_drbg_seed(&mCtrDrbg, mbedtls_entropy_func, &mEntropy, NULL, 0);
    VerifyOrExit(rval == 0, ;);

    rval = mbedtls_ssl_config_defaults(&mConf, mClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
                                       MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT);
    VerifyOrExit(rval == 0, ;);

    mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mCtrDrbg);
    mbedtls_ssl_conf_min_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
    mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
    mbedtls_ssl_conf_ciphersuites(&mConf, ciphersuites);
    mbedtls_ssl_conf_export_keys_cb(&mConf, HandleMbedtlsExportKeys, this);
    mbedtls_ssl_conf_handshake_timeout(&mConf, 8000, 60000);
    mbedtls_ssl_conf_dbg(&mConf, HandleMbedtlsDebug, stdout);

    if (!mClient)
    {
        mbedtls_ssl_cookie_init(&mCookieCtx);

        rval = mbedtls_ssl_cookie_setup(&mCookieCtx, mbedtls_ctr_drbg_random, &mCtrDrbg);
        VerifyOrExit(rval == 0, ;);

        mbedtls_ssl_conf_dtls_cookies(&mConf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &mCookieCtx);
    }

    rval = mbedtls_ssl_setup(&mSsl, &mConf);
    VerifyOrExit(rval == 0, ;);

    mbedtls_ssl_set_bio(&mSsl, this, &HandleMbedtlsTransmit, HandleMbedtlsReceive, NULL);
    mbedtls_ssl_set_timer_cb(&mSsl, this, &HandleMbedtlsSetTimer, HandleMbedtlsGetTimer);

    rval = mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
    VerifyOrExit(rval == 0, ;);

    mStarted = true;
    Process();

    otLogInfoMeshCoP("DTLS started\r\n");

exit:
    return MapError(rval);
}

ThreadError Dtls::Stop(void)
{
    mStarted = false;
    mbedtls_ssl_close_notify(&mSsl);
    mbedtls_ssl_free(&mSsl);
    mbedtls_ssl_config_free(&mConf);
    mbedtls_ctr_drbg_free(&mCtrDrbg);
    mbedtls_entropy_free(&mEntropy);
    mbedtls_ssl_cookie_free(&mCookieCtx);
    return kThreadError_None;
}

ThreadError Dtls::SetPsk(const uint8_t *aPsk, uint8_t aPskLength)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(aPskLength <= sizeof(mPsk), error = kThreadError_InvalidArgs);

    memcpy(mPsk, aPsk, aPskLength);
    mPskLength = aPskLength;

exit:
    return error;
}

ThreadError Dtls::SetClientId(const uint8_t *aClientId, uint8_t aLength)
{
    int rval = mbedtls_ssl_set_client_transport_id(&mSsl, aClientId, aLength);
    return MapError(rval);
}

bool Dtls::IsConnected(void)
{
    return mSsl.state == MBEDTLS_SSL_HANDSHAKE_OVER;
}

ThreadError Dtls::Send(const uint8_t *aBuf, uint16_t aLength)
{
    int rval = mbedtls_ssl_write(&mSsl, aBuf, aLength);
    return MapError(rval);
}

ThreadError Dtls::Receive(Message &aMessage, uint16_t aOffset, uint16_t aLength)
{
    mReceiveMessage = &aMessage;
    mReceiveOffset = aOffset;
    mReceiveLength = aLength;

    Process();

    return kThreadError_None;
}

int Dtls::HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsTransmit(aBuf, aLength);
}

int Dtls::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength)
{
    ThreadError error;
    int rval = 0;

    otLogInfoMeshCoP("Dtls::HandleMbedtlsTransmit\r\n");

    error = mSendHandler(mContext, aBuf, (uint16_t)aLength);

    switch (error)
    {
    case kThreadError_None:
        rval = static_cast<int>(aLength);
        break;

    case kThreadError_NoBufs:
        rval = MBEDTLS_ERR_SSL_WANT_WRITE;
        break;

    default:
        assert(false);
        break;
    }

    return rval;
}

int Dtls::HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsReceive(aBuf, aLength);
}

int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength)
{
    int rval;

    otLogInfoMeshCoP("Dtls::HandleMbedtlsReceive\r\n");

    VerifyOrExit(mReceiveMessage != NULL && mReceiveLength != 0, rval = MBEDTLS_ERR_SSL_WANT_READ);

    if (aLength > mReceiveLength)
    {
        aLength = mReceiveLength;
    }

    rval = (int)mReceiveMessage->Read(mReceiveOffset, (uint16_t)aLength, aBuf);
    mReceiveOffset += static_cast<uint16_t>(rval);
    mReceiveLength -= static_cast<uint16_t>(rval);

exit:
    return rval;
}

int Dtls::HandleMbedtlsGetTimer(void *aContext)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsGetTimer();
}

int Dtls::HandleMbedtlsGetTimer(void)
{
    int rval;

    otLogInfoMeshCoP("Dtls::HandleMbedtlsGetTimer\r\n");

    if (!mTimerSet)
    {
        rval = -1;
    }
    else if (!mTimer.IsRunning())
    {
        rval = 2;
    }
    else if (static_cast<int32_t>(mTimerIntermediate - Timer::GetNow()) <= 0)
    {
        rval = 1;
    }
    else
    {
        rval = 0;
    }

    return rval;
}

void Dtls::HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish)
{
    static_cast<Dtls *>(aContext)->HandleMbedtlsSetTimer(aIntermediate, aFinish);
}

void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish)
{
    otLogInfoMeshCoP("Dtls::SetTimer\r\n");

    if (aFinish == 0)
    {
        mTimerSet = false;
        mTimer.Stop();
    }
    else
    {
        mTimerSet = true;
        mTimer.Start(aFinish);
        mTimerIntermediate = Timer::GetNow() + aIntermediate;
    }
}

int Dtls::HandleMbedtlsExportKeys(void *aContext, const unsigned char *aMasterSecret, const unsigned char *aKeyBlock,
                                  size_t aMacLength, size_t aKeyLength, size_t aIvLength)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock,
                                                                  aMacLength, aKeyLength, aIvLength);
}

int Dtls::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, const unsigned char *aKeyBlock,
                                  size_t aMacLength, size_t aKeyLength, size_t aIvLength)
{
    uint8_t kek[Crypto::Sha256::kHashSize];
    Crypto::Sha256 sha256;

    sha256.Start();
    sha256.Update(aKeyBlock, static_cast<uint16_t>(aMacLength + aKeyLength + aIvLength));
    sha256.Finish(kek);

    mNetif.GetKeyManager().SetKek(kek);

    otLogInfoMeshCoP("Generated KEK\r\n");

    (void)aMasterSecret;
    return 0;
}

void Dtls::HandleTimer(void *aContext)
{
    static_cast<Dtls *>(aContext)->HandleTimer();
}

void Dtls::HandleTimer(void)
{
    Process();
}

void Dtls::Process(void)
{
    uint8_t buf[MBEDTLS_SSL_MAX_CONTENT_LEN];
    int rval;

    while (mStarted)
    {
        if (mSsl.state != MBEDTLS_SSL_HANDSHAKE_OVER)
        {
            rval = mbedtls_ssl_handshake(&mSsl);
        }
        else
        {
            rval = mbedtls_ssl_read(&mSsl, buf, sizeof(buf));
        }

        if (rval > 0)
        {
            mReceiveHandler(mContext, buf, static_cast<uint16_t>(rval));
        }
        else if (rval == 0 || rval == MBEDTLS_ERR_SSL_WANT_READ || rval == MBEDTLS_ERR_SSL_WANT_WRITE)
        {
            break;
        }
        else
        {
            mbedtls_ssl_session_reset(&mSsl);
            mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
            break;
        }
    }
}

ThreadError Dtls::MapError(int rval)
{
    ThreadError error = kThreadError_None;

    switch (rval)
    {
    case MBEDTLS_ERR_SSL_BAD_INPUT_DATA:
        error = kThreadError_InvalidArgs;
        break;

    case MBEDTLS_ERR_SSL_ALLOC_FAILED:
        error = kThreadError_NoBufs;
        break;

    default:
        assert(rval >= 0);
        break;
    }

    return error;
}

void Dtls::HandleMbedtlsDebug(void *ctx, int level, const char *file, int line, const char *str)
{
    otLogInfoMeshCoP("%s:%04d: %s\r\n", file, line, str);
    (void)ctx;
    (void)level;
    (void)file;
    (void)line;
    (void)str;
}

}  // namespace MeshCoP
}  // namespace Thread
<commit_msg>Send DTLS-Alert(close_notify) after receiving it from peer (#620)<commit_after>/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file implements the necessary hooks for mbedTLS.
 */

#include <assert.h>
#include <stdio.h>

#include <common/code_utils.hpp>
#include <common/encoding.hpp>
#include <common/logging.hpp>
#include <common/timer.hpp>
#include <crypto/sha256.hpp>
#include <meshcop/dtls.hpp>
#include <thread/thread_netif.hpp>

#include <mbedtls/debug.h>

namespace Thread {
namespace MeshCoP {

Dtls::Dtls(ThreadNetif &aNetif):
    mStarted(false),
    mTimer(aNetif.GetIp6().mTimerScheduler, &HandleTimer, this),
    mTimerIntermediate(0),
    mTimerSet(false),
    mNetif(aNetif)
{
}

ThreadError Dtls::Start(bool aClient, ReceiveHandler aReceiveHandler, SendHandler aSendHandler, void *aContext)
{
    static const int ciphersuites[2] = {0xC0FF, 0}; // EC-JPAKE cipher suite
    int rval;

    mReceiveHandler = aReceiveHandler;
    mSendHandler = aSendHandler;
    mContext = aContext;
    mClient = aClient;

    mbedtls_ssl_init(&mSsl);
    mbedtls_ssl_config_init(&mConf);
    mbedtls_ctr_drbg_init(&mCtrDrbg);
    mbedtls_entropy_init(&mEntropy);

    mbedtls_debug_set_threshold(4);

    // XXX: should set personalization data to hardware address
    rval = mbedtls_ctr_drbg_seed(&mCtrDrbg, mbedtls_entropy_func, &mEntropy, NULL, 0);
    VerifyOrExit(rval == 0, ;);

    rval = mbedtls_ssl_config_defaults(&mConf, mClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
                                       MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT);
    VerifyOrExit(rval == 0, ;);

    mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mCtrDrbg);
    mbedtls_ssl_conf_min_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
    mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
    mbedtls_ssl_conf_ciphersuites(&mConf, ciphersuites);
    mbedtls_ssl_conf_export_keys_cb(&mConf, HandleMbedtlsExportKeys, this);
    mbedtls_ssl_conf_handshake_timeout(&mConf, 8000, 60000);
    mbedtls_ssl_conf_dbg(&mConf, HandleMbedtlsDebug, stdout);

    if (!mClient)
    {
        mbedtls_ssl_cookie_init(&mCookieCtx);

        rval = mbedtls_ssl_cookie_setup(&mCookieCtx, mbedtls_ctr_drbg_random, &mCtrDrbg);
        VerifyOrExit(rval == 0, ;);

        mbedtls_ssl_conf_dtls_cookies(&mConf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &mCookieCtx);
    }

    rval = mbedtls_ssl_setup(&mSsl, &mConf);
    VerifyOrExit(rval == 0, ;);

    mbedtls_ssl_set_bio(&mSsl, this, &HandleMbedtlsTransmit, HandleMbedtlsReceive, NULL);
    mbedtls_ssl_set_timer_cb(&mSsl, this, &HandleMbedtlsSetTimer, HandleMbedtlsGetTimer);

    rval = mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
    VerifyOrExit(rval == 0, ;);

    mStarted = true;
    Process();

    otLogInfoMeshCoP("DTLS started\r\n");

exit:
    return MapError(rval);
}

ThreadError Dtls::Stop(void)
{
    mStarted = false;
    mbedtls_ssl_close_notify(&mSsl);
    mbedtls_ssl_free(&mSsl);
    mbedtls_ssl_config_free(&mConf);
    mbedtls_ctr_drbg_free(&mCtrDrbg);
    mbedtls_entropy_free(&mEntropy);
    mbedtls_ssl_cookie_free(&mCookieCtx);
    return kThreadError_None;
}

ThreadError Dtls::SetPsk(const uint8_t *aPsk, uint8_t aPskLength)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(aPskLength <= sizeof(mPsk), error = kThreadError_InvalidArgs);

    memcpy(mPsk, aPsk, aPskLength);
    mPskLength = aPskLength;

exit:
    return error;
}

ThreadError Dtls::SetClientId(const uint8_t *aClientId, uint8_t aLength)
{
    int rval = mbedtls_ssl_set_client_transport_id(&mSsl, aClientId, aLength);
    return MapError(rval);
}

bool Dtls::IsConnected(void)
{
    return mSsl.state == MBEDTLS_SSL_HANDSHAKE_OVER;
}

ThreadError Dtls::Send(const uint8_t *aBuf, uint16_t aLength)
{
    int rval = mbedtls_ssl_write(&mSsl, aBuf, aLength);
    return MapError(rval);
}

ThreadError Dtls::Receive(Message &aMessage, uint16_t aOffset, uint16_t aLength)
{
    mReceiveMessage = &aMessage;
    mReceiveOffset = aOffset;
    mReceiveLength = aLength;

    Process();

    return kThreadError_None;
}

int Dtls::HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsTransmit(aBuf, aLength);
}

int Dtls::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength)
{
    ThreadError error;
    int rval = 0;

    otLogInfoMeshCoP("Dtls::HandleMbedtlsTransmit\r\n");

    error = mSendHandler(mContext, aBuf, (uint16_t)aLength);

    switch (error)
    {
    case kThreadError_None:
        rval = static_cast<int>(aLength);
        break;

    case kThreadError_NoBufs:
        rval = MBEDTLS_ERR_SSL_WANT_WRITE;
        break;

    default:
        assert(false);
        break;
    }

    return rval;
}

int Dtls::HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsReceive(aBuf, aLength);
}

int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength)
{
    int rval;

    otLogInfoMeshCoP("Dtls::HandleMbedtlsReceive\r\n");

    VerifyOrExit(mReceiveMessage != NULL && mReceiveLength != 0, rval = MBEDTLS_ERR_SSL_WANT_READ);

    if (aLength > mReceiveLength)
    {
        aLength = mReceiveLength;
    }

    rval = (int)mReceiveMessage->Read(mReceiveOffset, (uint16_t)aLength, aBuf);
    mReceiveOffset += static_cast<uint16_t>(rval);
    mReceiveLength -= static_cast<uint16_t>(rval);

exit:
    return rval;
}

int Dtls::HandleMbedtlsGetTimer(void *aContext)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsGetTimer();
}

int Dtls::HandleMbedtlsGetTimer(void)
{
    int rval;

    otLogInfoMeshCoP("Dtls::HandleMbedtlsGetTimer\r\n");

    if (!mTimerSet)
    {
        rval = -1;
    }
    else if (!mTimer.IsRunning())
    {
        rval = 2;
    }
    else if (static_cast<int32_t>(mTimerIntermediate - Timer::GetNow()) <= 0)
    {
        rval = 1;
    }
    else
    {
        rval = 0;
    }

    return rval;
}

void Dtls::HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish)
{
    static_cast<Dtls *>(aContext)->HandleMbedtlsSetTimer(aIntermediate, aFinish);
}

void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish)
{
    otLogInfoMeshCoP("Dtls::SetTimer\r\n");

    if (aFinish == 0)
    {
        mTimerSet = false;
        mTimer.Stop();
    }
    else
    {
        mTimerSet = true;
        mTimer.Start(aFinish);
        mTimerIntermediate = Timer::GetNow() + aIntermediate;
    }
}

int Dtls::HandleMbedtlsExportKeys(void *aContext, const unsigned char *aMasterSecret, const unsigned char *aKeyBlock,
                                  size_t aMacLength, size_t aKeyLength, size_t aIvLength)
{
    return static_cast<Dtls *>(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock,
                                                                  aMacLength, aKeyLength, aIvLength);
}

int Dtls::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, const unsigned char *aKeyBlock,
                                  size_t aMacLength, size_t aKeyLength, size_t aIvLength)
{
    uint8_t kek[Crypto::Sha256::kHashSize];
    Crypto::Sha256 sha256;

    sha256.Start();
    sha256.Update(aKeyBlock, static_cast<uint16_t>(aMacLength + aKeyLength + aIvLength));
    sha256.Finish(kek);

    mNetif.GetKeyManager().SetKek(kek);

    otLogInfoMeshCoP("Generated KEK\r\n");

    (void)aMasterSecret;
    return 0;
}

void Dtls::HandleTimer(void *aContext)
{
    static_cast<Dtls *>(aContext)->HandleTimer();
}

void Dtls::HandleTimer(void)
{
    Process();
}

void Dtls::Process(void)
{
    uint8_t buf[MBEDTLS_SSL_MAX_CONTENT_LEN];
    int rval;

    while (mStarted)
    {
        if (mSsl.state != MBEDTLS_SSL_HANDSHAKE_OVER)
        {
            rval = mbedtls_ssl_handshake(&mSsl);
        }
        else
        {
            rval = mbedtls_ssl_read(&mSsl, buf, sizeof(buf));
        }

        if (rval > 0)
        {
            mReceiveHandler(mContext, buf, static_cast<uint16_t>(rval));
        }
        else if (rval == 0 || rval == MBEDTLS_ERR_SSL_WANT_READ || rval == MBEDTLS_ERR_SSL_WANT_WRITE)
        {
            break;
        }
        else
        {
            if (rval == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
            {
                mbedtls_ssl_close_notify(&mSsl);
            }

            mbedtls_ssl_session_reset(&mSsl);
            mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
            break;
        }
    }
}

ThreadError Dtls::MapError(int rval)
{
    ThreadError error = kThreadError_None;

    switch (rval)
    {
    case MBEDTLS_ERR_SSL_BAD_INPUT_DATA:
        error = kThreadError_InvalidArgs;
        break;

    case MBEDTLS_ERR_SSL_ALLOC_FAILED:
        error = kThreadError_NoBufs;
        break;

    default:
        assert(rval >= 0);
        break;
    }

    return error;
}

void Dtls::HandleMbedtlsDebug(void *ctx, int level, const char *file, int line, const char *str)
{
    otLogInfoMeshCoP("%s:%04d: %s\r\n", file, line, str);
    (void)ctx;
    (void)level;
    (void)file;
    (void)line;
    (void)str;
}

}  // namespace MeshCoP
}  // namespace Thread
<|endoftext|>


<commit_before><commit_msg>Fix, incorrect camera view matrix with non-rigid transformations (#2735)<commit_after><|endoftext|>