Implement fxcrt::Zip<>().
Take some ideas from C++23's zip_view<> to help improve performance
of some image manipulations over spans in the subsequent CLs.
Change-Id: Idfb062a8eda40cc11ed614542f2451652c168318
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/121351
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Tom Sepez <tsepez@google.com>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 70ad8c9..cba3e5c 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -140,6 +140,7 @@
"xml/cfx_xmlparser.h",
"xml/cfx_xmltext.cpp",
"xml/cfx_xmltext.h",
+ "zip.h",
]
configs += [
"../../:pdfium_strict_config",
@@ -251,6 +252,7 @@
"xml/cfx_xmlnode_unittest.cpp",
"xml/cfx_xmlparser_unittest.cpp",
"xml/cfx_xmltext_unittest.cpp",
+ "zip_unittest.cpp",
]
deps = [ ":unit_test_support" ]
pdfium_root_dir = "../../"
diff --git a/core/fxcrt/zip.h b/core/fxcrt/zip.h
new file mode 100644
index 0000000..9ee703a
--- /dev/null
+++ b/core/fxcrt/zip.h
@@ -0,0 +1,84 @@
+// Copyright 2024 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_ZIP_H_
+#define CORE_FXCRT_ZIP_H_
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "core/fxcrt/check_op.h"
+#include "core/fxcrt/compiler_specific.h"
+#include "core/fxcrt/span.h"
+
+namespace fxcrt {
+
+// Vastly simplified implementation of ideas from C++23 zip_view<>. Allows
+// safe traversal of two ranges with a single bounds check per iteration.
+
+// Example usage:
+// struct RGB { uint8_t r; uint8_t g; uint8_t b; };
+// const uint8_t gray[256] = { ... };
+// RGB rgbs[260];
+// for (auto [in, out] : Zip(gray, rgbs)) {
+// out.r = in;
+// out.g = in;
+// out.b = in;
+// }
+// which fills the first 256 elements of rgbs with the corresponding gray
+// value in each component, say.
+
+// Differences include:
+// - Only zips together two views instead of N.
+// - Size is determined by the first view, which must be smaller than the
+// second view.
+// - First view is presumed to be "input-like" and is const, second view is
+// presumed to be "output-like" and is non-const.
+// - Only those methods required to support use in a range-based for-loop
+// are provided.
+
+template <typename T, typename U>
+class ZipView {
+ public:
+ struct Iter {
+ bool operator==(const Iter& that) const { return first == that.first; }
+
+ bool operator!=(const Iter& that) const { return first != that.first; }
+
+ UNSAFE_BUFFER_USAGE Iter& operator++() {
+ // SAFETY: required from caller, enforced by UNSAFE_BUFFER_USAGE.
+ UNSAFE_BUFFERS(++first);
+ UNSAFE_BUFFERS(++second);
+ return *this;
+ }
+
+ std::pair<typename T::reference, typename U::reference> operator*() const {
+ return {*first, *second};
+ }
+
+ T::iterator first;
+ U::iterator second;
+ };
+
+ ZipView(T first, U second) : first_(first), second_(second) {
+ CHECK_LE(first.size(), second.size());
+ }
+
+ Iter begin() { return {first_.begin(), second_.begin()}; }
+ Iter end() { return {first_.end(), second_.end()}; }
+
+ private:
+ T first_;
+ U second_;
+};
+
+template <typename T, typename U>
+auto Zip(const T& first, U&& second) {
+ return ZipView(pdfium::span(first), pdfium::span(second));
+}
+
+} // namespace fxcrt
+
+#endif // CORE_FXCRT_ZIP_H_
diff --git a/core/fxcrt/zip_unittest.cpp b/core/fxcrt/zip_unittest.cpp
new file mode 100644
index 0000000..2bb010a
--- /dev/null
+++ b/core/fxcrt/zip_unittest.cpp
@@ -0,0 +1,45 @@
+// Copyright 2024 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "core/fxcrt/span.h"
+#include "core/fxcrt/zip.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAreArray;
+
+namespace fxcrt {
+
+TEST(Zip, EmptyZip) {
+ pdfium::span<const int> nothing;
+ int stuff[] = {1, 2, 3};
+
+ auto zip_nothing_nothing = Zip(nothing, nothing);
+ EXPECT_EQ(zip_nothing_nothing.begin(), zip_nothing_nothing.end());
+
+ auto zip_nothing_stuff = Zip(nothing, stuff);
+ EXPECT_EQ(zip_nothing_stuff.begin(), zip_nothing_stuff.end());
+}
+
+TEST(Zip, ActualZip) {
+ const int stuff[] = {1, 2, 3};
+ const int expected[] = {1, 2, 3, 0};
+ int output[4] = {};
+
+ for (auto [in, out] : Zip(stuff, output)) {
+ out = in;
+ }
+ EXPECT_THAT(output, ElementsAreArray(expected));
+}
+
+TEST(Zip, BadArgumentsZip) {
+ pdfium::span<const int> nothing;
+ int stuff[] = {1, 2, 3};
+
+ EXPECT_DEATH(Zip(stuff, nothing), ".*");
+}
+
+} // namespace fxcrt