| 744 | */ |
| 745 | template <typename F, typename U = std::invoke_result_t<F, T>> |
| 746 | static ObjectPtr<Object> MapHelper(ObjectPtr<Object> data, F fmap) { |
| 747 | if (data == nullptr) { |
| 748 | return nullptr; |
| 749 | } |
| 750 | |
| 751 | TVM_FFI_ICHECK(data->IsInstance<ArrayObj>()); |
| 752 | |
| 753 | constexpr bool is_same_output_type = std::is_same_v<T, U>; |
| 754 | |
| 755 | if constexpr (is_same_output_type) { |
| 756 | if (data.unique()) { |
| 757 | // Mutate-in-place path. Only allowed if the output type U is |
| 758 | // the same as type T, we have a mutable this*, and there are |
| 759 | // no other shared copies of the array. |
| 760 | auto arr = static_cast<ArrayObj*>(data.get()); |
| 761 | for (auto it = arr->MutableBegin(); it != arr->MutableEnd(); it++) { |
| 762 | T value = details::AnyUnsafe::CopyFromAnyViewAfterCheck<T>(*it); |
| 763 | // reset the original value to nullptr, to ensure unique ownership |
| 764 | it->reset(); |
| 765 | T mapped = fmap(std::move(value)); |
| 766 | *it = std::move(mapped); |
| 767 | } |
| 768 | return data; |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | constexpr bool compatible_types = is_valid_iterator_v<T, U*> || is_valid_iterator_v<U, T*>; |
| 773 | |
| 774 | ObjectPtr<ArrayObj> output = nullptr; |
| 775 | auto arr = static_cast<ArrayObj*>(data.get()); |
| 776 | |
| 777 | auto it = arr->begin(); |
| 778 | if constexpr (compatible_types) { |
| 779 | // Copy-on-write path, if the output Array<U> might be |
| 780 | // represented by the same underlying array as the existing |
| 781 | // Array<T>. Typically, this is for functions that map `T` to |
| 782 | // `T`, but can also apply to functions that map `T` to |
| 783 | // `Optional<T>`, or that map `T` to a subclass or superclass of |
| 784 | // `T`. |
| 785 | bool all_identical = true; |
| 786 | for (; it != arr->end(); it++) { |
| 787 | U mapped = fmap(details::AnyUnsafe::CopyFromAnyViewAfterCheck<T>(*it)); |
| 788 | if (!(*it).same_as(mapped)) { |
| 789 | // At least one mapped element is different than the |
| 790 | // original. Therefore, prepare the output array, |
| 791 | // consisting of any previous elements that had mapped to |
| 792 | // themselves (if any), and the element that didn't map to |
| 793 | // itself. |
| 794 | // |
| 795 | // We cannot use `U()` as the default object, as `U` may be |
| 796 | // a non-nullable type. Since the default `Any()` |
| 797 | // will be overwritten before returning, all objects will be |
| 798 | // of type `U` for the calling scope. |
| 799 | all_identical = false; |
| 800 | output = ArrayObj::CreateRepeated(static_cast<int64_t>(arr->size()), Any()); |
| 801 | output->InitRange(0, arr->begin(), it); |
| 802 | output->SetItem(it - arr->begin(), std::move(mapped)); |
| 803 | it++; |
nothing calls this directly
no test coverage detected