Add Enumerate() a C++ version of Python's builtin.

It allows code like the following:

  for (auto [i, value] : container) { ... }

Bug: dawn:2222
Change-Id: I0fc95db690f738e742da1c8e661b8f8a98d03f3b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/162502
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/common/BUILD.gn b/src/dawn/common/BUILD.gn
index 6d9d917..48c12fe 100644
--- a/src/dawn/common/BUILD.gn
+++ b/src/dawn/common/BUILD.gn
@@ -255,6 +255,7 @@
       "CoreFoundationRef.h",
       "DynamicLib.cpp",
       "DynamicLib.h",
+      "Enumerator.h",
       "FutureUtils.h",
       "GPUInfo.cpp",
       "GPUInfo.h",
diff --git a/src/dawn/common/CMakeLists.txt b/src/dawn/common/CMakeLists.txt
index f676382..cfbcce8 100644
--- a/src/dawn/common/CMakeLists.txt
+++ b/src/dawn/common/CMakeLists.txt
@@ -58,6 +58,7 @@
     "CoreFoundationRef.h"
     "DynamicLib.cpp"
     "DynamicLib.h"
+    "Enumerator.h"
     "FutureUtils.h"
     "GPUInfo.cpp"
     "GPUInfo.h"
diff --git a/src/dawn/common/Enumerator.h b/src/dawn/common/Enumerator.h
new file mode 100644
index 0000000..7227ca1
--- /dev/null
+++ b/src/dawn/common/Enumerator.h
@@ -0,0 +1,83 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// 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.
+
+#ifndef SRC_DAWN_COMMON_ENUMERATOR_H_
+#define SRC_DAWN_COMMON_ENUMERATOR_H_
+
+#include <type_traits>
+#include <utility>
+
+namespace dawn {
+
+// An iterator over a range that gives both the index and the value. It can be used like so:
+//
+//   ityp::span<Index, Value> span = ...;
+//   for (auto [i, value] : Enumerate(span)) {
+//       // Use i that's of type Index.
+//       // Use value that's of type Value&.
+//   }
+template <typename Index, typename Value>
+class EnumerateRange final {
+  public:
+    EnumerateRange(Index size, Value* begin) : mSize(size), mBegin(begin) {}
+
+    class Iterator final {
+      public:
+        Iterator(Index index, Value* value) : mIndex(index), mValue(value) {}
+        bool operator==(const Iterator& other) const { return other.mIndex == mIndex; }
+        bool operator!=(const Iterator& other) const { return !(*this == other); }
+        Iterator& operator++() {
+            mIndex++;
+            mValue++;
+            return *this;
+        }
+        std::pair<Index, Value&> operator*() const { return {mIndex, *mValue}; }
+
+      private:
+        Index mIndex;
+        Value* mValue;
+    };
+
+    Iterator begin() const { return Iterator(Index{}, mBegin); }
+    // Note that iterator comparison only uses mIndex, so we can save the computation of mValue for
+    // the end() iterator.
+    Iterator end() const { return Iterator(mSize, nullptr); }
+
+  private:
+    Index mSize;
+    Value* mBegin;
+};
+
+template <typename T,
+          typename Index = decltype(std::declval<T>().size()),
+          typename Value = std::remove_pointer_t<decltype(std::declval<T>().data())>>
+EnumerateRange<Index, Value> Enumerate(T& v) {
+    return {v.size(), v.data()};
+}
+}  // namespace dawn
+
+#endif  // SRC_DAWN_COMMON_ENUMERATOR_H_
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index 15b5c6d..8080bb4 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -308,6 +308,7 @@
     "unittests/ContentLessObjectCacheTests.cpp",
     "unittests/EnumClassBitmasksTests.cpp",
     "unittests/EnumMaskIteratorTests.cpp",
+    "unittests/EnumeratorTests.cpp",
     "unittests/ErrorTests.cpp",
     "unittests/FeatureTests.cpp",
     "unittests/GPUInfoTests.cpp",
diff --git a/src/dawn/tests/unittests/EnumeratorTests.cpp b/src/dawn/tests/unittests/EnumeratorTests.cpp
new file mode 100644
index 0000000..c20db30
--- /dev/null
+++ b/src/dawn/tests/unittests/EnumeratorTests.cpp
@@ -0,0 +1,136 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// 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.
+
+#include <array>
+#include <vector>
+
+#include "dawn/common/Compiler.h"
+#include "dawn/common/Enumerator.h"
+#include "dawn/common/ityp_array.h"
+#include "dawn/common/ityp_span.h"
+#include "dawn/common/ityp_vector.h"
+#include "gtest/gtest.h"
+
+namespace dawn {
+namespace {
+
+class EnumeratorTest : public testing::Test {
+  protected:
+    template <typename Index, typename Thing, typename Value>
+    void Check(Thing thingToEnumerate, const Value* values) {
+        size_t i = 0;
+        for (auto [index, value] : Enumerate(thingToEnumerate)) {
+            ASSERT_EQ(index, Index(i));
+            ASSERT_EQ(value, values[i]);
+            i++;
+        }
+    }
+
+    template <typename Thing>
+    void CheckEmpty(Thing thingToEnumerate) {
+        for (auto [index, value] : Enumerate(thingToEnumerate)) {
+            DAWN_UNUSED(index);
+            DAWN_UNUSED(value);
+            FAIL();
+        }
+    }
+
+    using Int = TypedInteger<struct IntT, size_t>;
+};
+
+// Test that Enumerate works with std::array
+TEST_F(EnumeratorTest, StdArray) {
+    // Empty array
+    std::array<uint32_t, 0> emptyThing{};
+    CheckEmpty(emptyThing);
+
+    // Non-empty array
+    std::array<uint32_t, 3> thing = {37, 45, 67};
+    Check<size_t>(thing, thing.data());
+}
+
+// Test that Enumerate works with std::vector
+TEST_F(EnumeratorTest, StdVector) {
+    // Empty vector
+    std::vector<uint32_t> emptyThing;
+    CheckEmpty(emptyThing);
+
+    // Non-empty vector
+    std::vector<uint32_t> thing = {{37, 45, 67}};
+    Check<size_t>(thing, thing.data());
+}
+
+// Test that Enumerate works with ityp::array
+TEST_F(EnumeratorTest, ITypArray) {
+    // Empty array
+    ityp::array<Int, uint32_t, 0> emptyThing;
+    CheckEmpty(emptyThing);
+
+    // Non-empty array
+    ityp::array<Int, uint32_t, 3> thing = {37u, 45u, 67u};
+    Check<Int>(thing, thing.data());
+}
+
+// Test that Enumerate works with ityp::span
+TEST_F(EnumeratorTest, ITypSpan) {
+    // Empty span
+    ityp::span<Int, uint32_t> emptyThing;
+    CheckEmpty(emptyThing);
+
+    // Empty span for a non-null pointer.
+    uint32_t whatever = 923847;
+    auto emptyThingWithGarbagePointer = ityp::SpanFromUntyped<Int>(&whatever, 0);
+    CheckEmpty(emptyThingWithGarbagePointer);
+
+    // Non-empty span
+    std::array<uint32_t, 3> backingArray = {{37, 45, 67}};
+    auto thing = ityp::SpanFromUntyped<Int>(backingArray.data(), 3);
+    Check<Int>(thing, thing.data());
+}
+
+// Test that Enumerate works with ityp::vector
+TEST_F(EnumeratorTest, ITypVector) {
+    // Empty vector
+    ityp::vector<Int, uint32_t> emptyThing;
+    CheckEmpty(emptyThing);
+
+    // Non-empty vector
+    ityp::vector<Int, uint32_t> thing = {{37, 45, 67}};
+    Check<Int>(thing, thing.data());
+}
+
+// Test that Enumerate(const container) will give const reference to the values.
+TEST_F(EnumeratorTest, ConstContainer) {
+    // Non-empty array
+    const ityp::array<Int, uint32_t, 3> values = {37u, 45u, 67u};
+    for (auto [i, value] : Enumerate(values)) {
+        static_assert(std::is_same_v<decltype(value), const uint32_t&>);
+    }
+}
+
+}  // anonymous namespace
+}  // namespace dawn