nsresult SVGPathSegListSMILType::Assign(SMILValue& aDest,
                                        const SMILValue& aSrc) const {
  MOZ_ASSERT(aDest.mType == aSrc.mType, "Incompatible SMIL types");
  MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL value");

  const SVGPathDataAndInfo* src =
      static_cast<const SVGPathDataAndInfo*>(aSrc.mU.mPtr);
  SVGPathDataAndInfo* dest = static_cast<SVGPathDataAndInfo*>(aDest.mU.mPtr);

  return dest->CopyFrom(*src);
}
nsSMILValue
SVGAnimatedPathSegList::SMILAnimatedPathSegList::GetBaseValue() const
{
  // To benefit from Return Value Optimization and avoid copy constructor calls
  // due to our use of return-by-value, we must return the exact same object
  // from ALL return points. This function must only return THIS variable:
  nsSMILValue val;

  nsSMILValue tmp(SVGPathSegListSMILType::Singleton());
  SVGPathDataAndInfo *list = static_cast<SVGPathDataAndInfo*>(tmp.mU.mPtr);
  nsresult rv = list->CopyFrom(mVal->mBaseVal);
  if (NS_SUCCEEDED(rv)) {
    list->SetElement(mElement);
    val = Move(tmp);
  }
  return val;
}
nsresult
SVGAnimatedPathSegList::
  SMILAnimatedPathSegList::ValueFromString(const nsAString& aStr,
                               const dom::SVGAnimationElement* /*aSrcElement*/,
                               nsSMILValue& aValue,
                               bool& aPreventCachingOfSandwich) const
{
  nsSMILValue val(SVGPathSegListSMILType::Singleton());
  SVGPathDataAndInfo *list = static_cast<SVGPathDataAndInfo*>(val.mU.mPtr);
  nsresult rv = list->SetValueFromString(aStr);
  if (NS_SUCCEEDED(rv)) {
    list->SetElement(mElement);
    aValue = Move(val);
  }
  aPreventCachingOfSandwich = false;
  return rv;
}
static PathInterpolationResult CanInterpolate(const SVGPathDataAndInfo& aStart,
                                              const SVGPathDataAndInfo& aEnd) {
  if (aStart.IsIdentity()) {
    return eCanInterpolate;
  }

  if (aStart.Length() != aEnd.Length()) {
    return eCannotInterpolate;
  }

  PathInterpolationResult result = eCanInterpolate;

  SVGPathDataAndInfo::const_iterator pStart = aStart.begin();
  SVGPathDataAndInfo::const_iterator pEnd = aEnd.begin();
  SVGPathDataAndInfo::const_iterator pStartDataEnd = aStart.end();
  SVGPathDataAndInfo::const_iterator pEndDataEnd = aEnd.end();

  while (pStart < pStartDataEnd && pEnd < pEndDataEnd) {
    uint32_t startType = SVGPathSegUtils::DecodeType(*pStart);
    uint32_t endType = SVGPathSegUtils::DecodeType(*pEnd);

    if (SVGPathSegUtils::IsArcType(startType) &&
        SVGPathSegUtils::IsArcType(endType) && ArcFlagsDiffer(pStart, pEnd)) {
      return eCannotInterpolate;
    }

    if (startType != endType) {
      if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) {
        return eCannotInterpolate;
      }

      result = eRequiresConversion;
    }

    pStart += 1 + SVGPathSegUtils::ArgCountForType(startType);
    pEnd += 1 + SVGPathSegUtils::ArgCountForType(endType);
  }

  MOZ_ASSERT(pStart <= pStartDataEnd && pEnd <= pEndDataEnd,
             "Iterated past end of buffer! (Corrupt path data?)");

  if (pStart != pStartDataEnd || pEnd != pEndDataEnd) {
    return eCannotInterpolate;
  }

  return result;
}
/**
 * Helper function for Add & Interpolate, to add multiples of two path-segment
 * lists.
 *
 * NOTE: aList1 and aList2 are assumed to have their segment-types and
 * segment-count match exactly (unless aList1 is an identity value).
 *
 * NOTE: aResult, the output list, is expected to either be an identity value
 * (in which case we'll grow it) *or* to already have the exactly right length
 * (e.g. in cases where aList1 and aResult are actually the same list).
 *
 * @param aCoeff1    The coefficient to use on the first path segment list.
 * @param aList1     The first path segment list. Allowed to be identity.
 * @param aCoeff2    The coefficient to use on the second path segment list.
 * @param aList2     The second path segment list.
 * @param [out] aResultSeg The resulting path segment list. Allowed to be
 *                         identity, in which case we'll grow it to the right
 *                         size. Also allowed to be the same list as aList1.
 */
static nsresult AddWeightedPathSegLists(double aCoeff1,
                                        const SVGPathDataAndInfo& aList1,
                                        double aCoeff2,
                                        const SVGPathDataAndInfo& aList2,
                                        SVGPathDataAndInfo& aResult) {
  MOZ_ASSERT(aCoeff1 >= 0.0 && aCoeff2 >= 0.0,
             "expecting non-negative coefficients");
  MOZ_ASSERT(!aList2.IsIdentity(), "expecting 2nd list to be non-identity");
  MOZ_ASSERT(aList1.IsIdentity() || aList1.Length() == aList2.Length(),
             "expecting 1st list to be identity or to have same "
             "length as 2nd list");
  MOZ_ASSERT(aResult.IsIdentity() || aResult.Length() == aList2.Length(),
             "expecting result list to be identity or to have same "
             "length as 2nd list");

  SVGPathDataAndInfo::const_iterator iter1, end1;
  if (aList1.IsIdentity()) {
    iter1 = end1 = nullptr;  // indicate that this is an identity list
  } else {
    iter1 = aList1.begin();
    end1 = aList1.end();
  }
  SVGPathDataAndInfo::const_iterator iter2 = aList2.begin();
  SVGPathDataAndInfo::const_iterator end2 = aList2.end();

  // Grow |aResult| if necessary. (NOTE: It's possible that aResult and aList1
  // are the same list, so this may implicitly resize aList1. That's fine,
  // because in that case, we will have already set iter1 to nullptr above, to
  // record that our first operand is an identity value.)
  if (aResult.IsIdentity()) {
    if (!aResult.SetLength(aList2.Length())) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    aResult.SetElement(aList2.Element());  // propagate target element info!
  }

  SVGPathDataAndInfo::iterator resultIter = aResult.begin();

  while ((!iter1 || iter1 != end1) && iter2 != end2) {
    AddWeightedPathSegs(aCoeff1, iter1, aCoeff2, iter2, resultIter);
  }
  MOZ_ASSERT(
      (!iter1 || iter1 == end1) && iter2 == end2 && resultIter == aResult.end(),
      "Very, very bad - path data corrupt");
  return NS_OK;
}