Reland "SkZip - synchronized indexing of several pointers"

This is a reland of f3c4a829c6

Original change's description:
> SkZip - synchronized indexing of several pointers
>
> This is the mechanism for syncing a bunch of things with
> contiguous memory like vector<>, array<> and SkSpan<>.
>
> In a following CL, a convenience function SkMakeZip will
> easily convert most containers into a zip.
>
> Change-Id: Icda5b1774ae21c4c163a663f6d2b0f119f63ccba
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/240200
> Commit-Queue: Herb Derby <herb@google.com>
> Reviewed-by: Ben Wagner <bungeman@google.com>

Change-Id: I7d1a91a9c35dde721147bb3c1f23c9b3a8d59d04
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/242476
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herb Derby 2019-09-09 11:36:39 -04:00 committed by Skia Commit-Bot
parent 1ba169135a
commit c44ee1a263
4 changed files with 188 additions and 3 deletions

View File

@ -405,6 +405,7 @@ skia_core_sources = [
"$_src/core/SkYUVPlanesCache.h",
"$_src/core/SkYUVASizeInfo.cpp",
"$_src/core/SkYUVMath.cpp",
"$_src/core/SkZip.h",
"$_src/image/SkImage.cpp",

View File

@ -9,9 +9,6 @@
#define SkSpan_DEFINED
#include <cstddef>
#include <string>
#include <vector>
#include "include/private/SkTo.h"
template <typename T>

79
src/core/SkZip.h Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkZip_DEFINED
#define SkZip_DEFINED
#include <cstddef>
#include <tuple>
#include <type_traits>
#include "include/private/SkTLogic.h"
#include "include/private/SkTemplates.h"
#include "include/private/SkTo.h"
#include "src/core/SkSpan.h"
// Take a list of things that can be pointers, and use them all in parallel. The iterators and
// accessor operator[] for the class produce a tuple of the items.
template<typename... Ts>
class SkZip {
using ReturnTuple = std::tuple<Ts&...>;
class Iterator {
public:
Iterator(const SkZip* zip, size_t index) : fZip{zip}, fIndex{index} { }
Iterator(const Iterator& that) : Iterator{ that.fZip, that.fIndex } { }
Iterator& operator++() { ++fIndex; return *this; }
Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; }
bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; }
ReturnTuple operator*() { return (*fZip)[fIndex]; }
private:
const SkZip* const fZip = nullptr;
size_t fIndex = 0;
};
public:
SkZip(size_t) = delete;
explicit SkZip(size_t size, Ts*... ts)
: fPointers{ts...}
, fSize{size} {}
template<typename... Us>
SkZip(const SkZip<Us...>& that)
: fPointers{that.fPointers}
, fSize{that.fSize} {}
ReturnTuple operator[](size_t i) const {
return this->index(i);
}
size_t size() const { return fSize; }
bool empty() const { return this->size() == 0; }
ReturnTuple front() const { return this->index(0); }
ReturnTuple back() const { return this->index(this->size() - 1); }
Iterator begin() const { return Iterator{this, 0}; }
Iterator end() const { return Iterator{this, this->size()}; }
template<size_t I> auto get() const { return SkMakeSpan(std::get<I>(fPointers), fSize); }
private:
ReturnTuple index(size_t i) const {
SkASSERT(this->size() > 0);
SkASSERT(i < this->size());
return indexDetail(i, skstd::make_index_sequence<sizeof...(Ts)>{});
}
template<std::size_t... Is>
ReturnTuple indexDetail(size_t i, skstd::index_sequence<Is...>) const {
return ReturnTuple((std::get<Is>(fPointers))[i]...);
}
std::tuple<Ts*...> fPointers;
size_t fSize;
};
#endif //SkZip_DEFINED

View File

@ -10,9 +10,12 @@
#include "src/core/SkSpan.h"
#include "src/core/SkTSearch.h"
#include "src/core/SkTSort.h"
#include "src/core/SkZip.h"
#include "tests/Test.h"
#include <array>
#include <initializer_list>
#include <tuple>
#include <vector>
class RefClass : public SkRefCnt {
@ -211,3 +214,108 @@ DEF_TEST(SkMakeSpan, reporter) {
REPORTER_ASSERT(reporter, s[3] == 100);
}
}
DEF_TEST(SkZip, reporter) {
uint16_t A[] = {1, 2, 3, 4};
const float B[] = {10.f, 20.f, 30.f, 40.f};
std::vector<int> C = {{20, 30, 40, 50}};
std::array<int, 4> D = {{100, 200, 300, 400}};
SkSpan<int> S = SkMakeSpan(C);
// Check SkZip calls
SkZip<uint16_t, const float, int, int, int>
z{4, &A[0], &B[0], C.data(), D.data(), S.data()};
REPORTER_ASSERT(reporter, z.size() == 4);
REPORTER_ASSERT(reporter, !z.empty());
{
// Check front
auto t = z.front();
REPORTER_ASSERT(reporter, std::get<0>(t) == 1);
REPORTER_ASSERT(reporter, std::get<1>(t) == 10.f);
REPORTER_ASSERT(reporter, std::get<2>(t) == 20);
REPORTER_ASSERT(reporter, std::get<3>(t) == 100);
REPORTER_ASSERT(reporter, std::get<4>(t) == 20);
}
{
// Check back
auto t = z.back();
REPORTER_ASSERT(reporter, std::get<0>(t) == 4);
REPORTER_ASSERT(reporter, std::get<1>(t) == 40.f);
}
{
// Check ranged-for
int i = 0;
for (auto t : z) {
uint16_t a; float b; int c; int d; int s;
std::tie(a, b, c, d, s) = t;
REPORTER_ASSERT(reporter, a == A[i]);
REPORTER_ASSERT(reporter, b == B[i]);
REPORTER_ASSERT(reporter, c == C[i]);
REPORTER_ASSERT(reporter, d == D[i]);
REPORTER_ASSERT(reporter, s == S[i]);
i++;
}
REPORTER_ASSERT(reporter, i = 4);
}
// Check copy.
auto zz{z};
{
int i = 0;
for (auto t : zz) {
uint16_t a; float b; int c; int d; int s;
std::tie(a, b, c, d, s) = t;
REPORTER_ASSERT(reporter, a == A[i]);
REPORTER_ASSERT(reporter, b == B[i]);
REPORTER_ASSERT(reporter, c == C[i]);
REPORTER_ASSERT(reporter, d == D[i]);
REPORTER_ASSERT(reporter, s == S[i]);
i++;
}
REPORTER_ASSERT(reporter, i = 4);
}
// Check index getter
{
auto span = z.get<1>();
REPORTER_ASSERT(reporter, span[1] == 20.f);
}
// The following mutates the data.
{
// Check indexing
auto t = z[1];
REPORTER_ASSERT(reporter, std::get<0>(t) == 2);
REPORTER_ASSERT(reporter, std::get<1>(t) == 20.f);
REPORTER_ASSERT(reporter, std::get<2>(t) == 30);
REPORTER_ASSERT(reporter, std::get<3>(t) == 200);
REPORTER_ASSERT(reporter, std::get<4>(t) == 30);
// Check correct refs returned.
REPORTER_ASSERT(reporter, &std::get<0>(t) == &A[1]);
REPORTER_ASSERT(reporter, &std::get<1>(t) == &B[1]);
REPORTER_ASSERT(reporter, &std::get<2>(t) == &C[1]);
REPORTER_ASSERT(reporter, &std::get<3>(t) == &D[1]);
REPORTER_ASSERT(reporter, &std::get<4>(t) == &S[1]);
// Check assignment
std::get<0>(t) = 20;
// std::get<1>(t) = 300.f; // is const
std::get<2>(t) = 300;
std::get<3>(t) = 2000;
std::get<4>(t) = 300;
auto t1 = z[1];
REPORTER_ASSERT(reporter, std::get<0>(t1) == 20);
REPORTER_ASSERT(reporter, std::get<1>(t1) == 20.f);
REPORTER_ASSERT(reporter, std::get<2>(t1) == 300);
REPORTER_ASSERT(reporter, std::get<3>(t1) == 2000);
REPORTER_ASSERT(reporter, std::get<4>(t1) == 300);
}
}