#include "mbgl/style/expression/expression.hpp" #include #include #include #include #include namespace mbgl { namespace style { namespace expression { namespace { int normalizeIndex(int index, int length) noexcept { if (index < 0) { index += length; } return index; } } // namespace Slice::Slice(std::unique_ptr input_, std::unique_ptr fromIndex_, std::unique_ptr toIndex_) : Expression(Kind::Slice, input_->getType(), depsOf(input_) | depsOf(fromIndex_) | depsOf(toIndex_)), input(std::move(input_)), fromIndex(std::move(fromIndex_)), toIndex(std::move(toIndex_)) {} EvaluationResult Slice::evaluate(const EvaluationContext ¶ms) const { const EvaluationResult evaluatedInput = input->evaluate(params); if (!evaluatedInput) { return evaluatedInput.error(); } const EvaluationResult evaluatedFromIndex = fromIndex->evaluate(params); if (!fromIndex) { return evaluatedFromIndex.error(); } if (!evaluatedFromIndex->is()) { return EvaluationError{"Expected value to be of type number, but found " + toString(typeOf(*evaluatedFromIndex)) + " instead."}; } int fromIndexValue = static_cast(evaluatedFromIndex->get()); int toIndexValue = std::numeric_limits::max(); if (toIndex) { const EvaluationResult evaluatedToIndex = toIndex->evaluate(params); if (!evaluatedToIndex) { return evaluatedToIndex.error(); } if (!evaluatedToIndex->is()) { return EvaluationError{"Expected value to be of type number, but found " + toString(typeOf(*evaluatedFromIndex)) + " instead."}; } toIndexValue = static_cast(evaluatedToIndex->get()); } return evaluatedInput->match( [&](const std::string &s) { return evaluateForStringInput(s, fromIndexValue, toIndexValue); }, [&](const std::vector &v) { return evaluateForArrayInput(v, fromIndexValue, toIndexValue); }, [&](const auto &) -> EvaluationResult { return EvaluationError{"Expected first argument to be of type array or string, but found " + toString(typeOf(*evaluatedInput)) + " instead."}; }); } EvaluationResult Slice::evaluateForArrayInput(const std::vector &array, int fromIndexValue, int toIndexValue) const { int length = static_cast(array.size()); if (toIndexValue == std::numeric_limits::max()) { toIndexValue = length; } fromIndexValue = normalizeIndex(fromIndexValue, length); toIndexValue = normalizeIndex(toIndexValue, length); if (fromIndexValue >= toIndexValue) { return std::vector{}; } toIndexValue = std::min(toIndexValue, length); std::vector result; result.reserve(toIndexValue - fromIndexValue); for (int index = fromIndexValue; index < toIndexValue; ++index) { result.push_back(array[index]); } return result; } EvaluationResult Slice::evaluateForStringInput(const std::string &string, int fromIndexValue, int toIndexValue) const { size_t lengthBytes = string.size(); int length = static_cast(unicodeLengthOnValidatedUtf8(string)); size_t toIndexValueBytes = ([&]() { if (toIndexValue == std::numeric_limits::max()) { return lengthBytes; } else { int toIndexValueChars = normalizeIndex(toIndexValue, length); return getUnicodeCharacterOffsetOnValidatedUtf8(string, toIndexValueChars); } })(); int fromIndexValueChars = normalizeIndex(fromIndexValue, length); size_t fromIndexValueBytes = getUnicodeCharacterOffsetOnValidatedUtf8(string, fromIndexValueChars); if ((fromIndexValueBytes >= lengthBytes) || (fromIndexValueBytes >= toIndexValueBytes)) { return std::string{}; } return string.substr(fromIndexValueBytes, toIndexValueBytes - fromIndexValueBytes); } void Slice::eachChild(const std::function &visit) const { visit(*input); visit(*fromIndex); if (toIndex) { visit(*toIndex); } } using namespace mbgl::style::conversion; ParseResult Slice::parse(const Convertible &value, ParsingContext &ctx) { assert(isArray(value)); std::size_t length = arrayLength(value); if (length != 3 && length != 4) { ctx.error("Expected 2 or 3 arguments, but found " + util::toString(length - 1) + " instead."); return ParseResult(); } ParseResult input = ctx.parse(arrayMember(value, 1), 1); ParseResult fromIndex = ctx.parse(arrayMember(value, 2), 2); ParseResult toIndex = length == 4 ? ctx.parse(arrayMember(value, 3), 3) : ParseResult(); if (!input || !fromIndex) { return ParseResult(); } return ParseResult( std::make_unique(std::move(*input), std::move(*fromIndex), toIndex ? std::move(*toIndex) : nullptr)); } bool Slice::operator==(const Expression &e) const noexcept { if (e.getKind() == Kind::Slice) { auto rhs = static_cast(&e); const auto toIndexEqual = (toIndex && rhs->toIndex && *toIndex == *(rhs->toIndex)) || (!toIndex && !rhs->toIndex); return *input == *(rhs->input) && *fromIndex == *(rhs->fromIndex) && toIndexEqual; } return false; } std::vector> Slice::possibleOutputs() const { return {std::nullopt}; } } // namespace expression } // namespace style } // namespace mbgl