|
| 1 | +//===- Matrix.h - Two-dimensional Container with View -----------*- C++ -*-===// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | + |
| 9 | +#ifndef LLVM_ADT_MATRIX_H |
| 10 | +#define LLVM_ADT_MATRIX_H |
| 11 | + |
| 12 | +#include "llvm/ADT/ArrayRef.h" |
| 13 | +#include "llvm/ADT/SmallVector.h" |
| 14 | + |
| 15 | +namespace llvm { |
| 16 | +template <typename T, size_t M, size_t NStorageInline> class JaggedArrayView; |
| 17 | + |
| 18 | +/// Due to the SmallVector infrastructure using SmallVectorAlignmentAndOffset |
| 19 | +/// that depends on the exact data layout, no derived classes can have extra |
| 20 | +/// members. |
| 21 | +template <typename T, size_t N> |
| 22 | +struct MatrixStorageBase : public SmallVectorImpl<T>, SmallVectorStorage<T, N> { |
| 23 | + MatrixStorageBase() : SmallVectorImpl<T>(N) {} |
| 24 | + MatrixStorageBase(size_t Size) : SmallVectorImpl<T>(N) { resize(Size); } |
| 25 | + ~MatrixStorageBase() { destroy_range(this->begin(), this->end()); } |
| 26 | + MatrixStorageBase(const MatrixStorageBase &RHS) : SmallVectorImpl<T>(N) { |
| 27 | + if (!RHS.empty()) |
| 28 | + SmallVectorImpl<T>::operator=(RHS); |
| 29 | + } |
| 30 | + MatrixStorageBase(MatrixStorageBase &&RHS) : SmallVectorImpl<T>(N) { |
| 31 | + if (!RHS.empty()) |
| 32 | + SmallVectorImpl<T>::operator=(::std::move(RHS)); |
| 33 | + } |
| 34 | + using SmallVectorImpl<T>::size; |
| 35 | + using SmallVectorImpl<T>::resize; |
| 36 | + using SmallVectorImpl<T>::append; |
| 37 | + using SmallVectorImpl<T>::erase; |
| 38 | + using SmallVectorImpl<T>::destroy_range; |
| 39 | + |
| 40 | + T *begin() const { return const_cast<T *>(SmallVectorImpl<T>::begin()); } |
| 41 | + T *end() const { return const_cast<T *>(SmallVectorImpl<T>::end()); } |
| 42 | +}; |
| 43 | + |
| 44 | +/// A two-dimensional container storage, whose upper bound on the number of |
| 45 | +/// columns should be known ahead of time. Not menat to be used directly: the |
| 46 | +/// primary usage API is MatrixView. |
| 47 | +template <typename T, |
| 48 | + size_t N = CalculateSmallVectorDefaultInlinedElements<T>::value> |
| 49 | +class MatrixStorage { |
| 50 | +public: |
| 51 | + MatrixStorage() = delete; |
| 52 | + MatrixStorage(size_t NRows, size_t NCols) |
| 53 | + : Base(NRows * NCols), NCols(NCols) {} |
| 54 | + MatrixStorage(size_t NCols) : Base(), NCols(NCols) {} |
| 55 | + |
| 56 | + size_t size() const { return Base.size(); } |
| 57 | + bool empty() const { return !size(); } |
| 58 | + size_t getNumRows() const { return size() / NCols; } |
| 59 | + size_t getNumCols() const { return NCols; } |
| 60 | + void setNumCols(size_t NCols) { |
| 61 | + assert(empty() && "Column-resizing a non-empty MatrixStorage"); |
| 62 | + this->NCols = NCols; |
| 63 | + } |
| 64 | + void resize(size_t NRows) { Base.resize(NCols * NRows); } |
| 65 | + |
| 66 | +protected: |
| 67 | + template <typename U, size_t M, size_t NStorageInline> |
| 68 | + friend class JaggedArrayView; |
| 69 | + |
| 70 | + T *begin() const { return Base.begin(); } |
| 71 | + T *rowFromIdx(size_t RowIdx, size_t Offset = 0) const { |
| 72 | + return begin() + RowIdx * NCols + Offset; |
| 73 | + } |
| 74 | + std::pair<size_t, size_t> idxFromRow(T *Ptr) const { |
| 75 | + assert(Ptr >= begin() && "Internal error"); |
| 76 | + size_t Offset = (Ptr - begin()) % NCols; |
| 77 | + return {(Ptr - begin()) / NCols, Offset}; |
| 78 | + } |
| 79 | + |
| 80 | + // If Arg.size() < NCols, the number of columns won't be changed, and the |
| 81 | + // difference is default-constructed. |
| 82 | + void addRow(const SmallVectorImpl<T> &Arg) { |
| 83 | + assert(Arg.size() <= NCols && |
| 84 | + "MatrixStorage has insufficient number of columns"); |
| 85 | + size_t Diff = NCols - Arg.size(); |
| 86 | + Base.append(Arg.begin(), Arg.end()); |
| 87 | + Base.append(Diff, T()); |
| 88 | + } |
| 89 | + |
| 90 | +private: |
| 91 | + MatrixStorageBase<T, N> Base; |
| 92 | + size_t NCols; |
| 93 | +}; |
| 94 | + |
| 95 | +/// MutableArrayRef with a copy-assign, and extra APIs. |
| 96 | +template <typename T> |
| 97 | +struct [[nodiscard]] MutableRowView : public MutableArrayRef<T> { |
| 98 | + using pointer = typename MutableArrayRef<T>::pointer; |
| 99 | + using iterator = typename MutableArrayRef<T>::iterator; |
| 100 | + using const_iterator = typename MutableArrayRef<T>::const_iterator; |
| 101 | + |
| 102 | + MutableRowView() = delete; |
| 103 | + MutableRowView(pointer Data, size_t Length) |
| 104 | + : MutableArrayRef<T>(Data, Length) {} |
| 105 | + MutableRowView(iterator Begin, iterator End) |
| 106 | + : MutableArrayRef<T>(Begin, End) {} |
| 107 | + MutableRowView(const_iterator Begin, const_iterator End) |
| 108 | + : MutableArrayRef<T>(Begin, End) {} |
| 109 | + MutableRowView(MutableArrayRef<T> Other) |
| 110 | + : MutableArrayRef<T>(Other.data(), Other.size()) {} |
| 111 | + MutableRowView(const SmallVectorImpl<T> &Vec) : MutableArrayRef<T>(Vec) {} |
| 112 | + |
| 113 | + using MutableArrayRef<T>::size; |
| 114 | + using MutableArrayRef<T>::data; |
| 115 | + |
| 116 | + T &back() const { return MutableArrayRef<T>::back(); } |
| 117 | + T &front() const { return MutableArrayRef<T>::front(); } |
| 118 | + MutableRowView<T> drop_back(size_t N = 1) const { // NOLINT |
| 119 | + return MutableArrayRef<T>::drop_back(N); |
| 120 | + } |
| 121 | + MutableRowView<T> drop_front(size_t N = 1) const { // NOLINT |
| 122 | + return MutableArrayRef<T>::drop_front(N); |
| 123 | + } |
| 124 | + // This slice is different from the MutableArrayRef slice, and specifies a |
| 125 | + // Begin and End index, instead of a Begin and Length. |
| 126 | + MutableRowView<T> slice(size_t Begin, size_t End) { |
| 127 | + return MutableArrayRef<T>::slice(Begin, End - Begin); |
| 128 | + } |
| 129 | + void pop_back(size_t N = 1) { // NOLINT |
| 130 | + this->Length -= N; |
| 131 | + } |
| 132 | + void pop_front(size_t N = 1) { // NOLINT |
| 133 | + this->Data += N; |
| 134 | + this->Length -= N; |
| 135 | + } |
| 136 | + |
| 137 | + MutableRowView &operator=(const SmallVectorImpl<T> &Vec) { |
| 138 | + copy_assign(Vec.begin(), Vec.end()); |
| 139 | + return *this; |
| 140 | + } |
| 141 | + MutableRowView &operator=(std::initializer_list<T> IL) { |
| 142 | + copy_assign(IL.begin(), IL.end()); |
| 143 | + return *this; |
| 144 | + } |
| 145 | + |
| 146 | + void swap(MutableRowView<T> &Other) { |
| 147 | + std::swap(this->Data, Other.Data); |
| 148 | + std::swap(this->Length, Other.Length); |
| 149 | + } |
| 150 | + |
| 151 | +protected: |
| 152 | + void copy_assign(iterator Begin, iterator End) { // NOLINT |
| 153 | + std::uninitialized_copy(Begin, End, data()); |
| 154 | + this->Length = End - Begin; |
| 155 | + } |
| 156 | + void copy_assign(const_iterator Begin, const_iterator End) { // NOLINT |
| 157 | + std::uninitialized_copy(Begin, End, data()); |
| 158 | + this->Length = End - Begin; |
| 159 | + } |
| 160 | +}; |
| 161 | + |
| 162 | +/// The primary usage API of MatrixStorage. Abstracts out indexing-arithmetic, |
| 163 | +/// eliminating memory operations on the underlying data. Supports |
| 164 | +/// variable-length columns. |
| 165 | +template <typename T, |
| 166 | + size_t N = CalculateSmallVectorDefaultInlinedElements<T>::value, |
| 167 | + size_t NStorageInline = |
| 168 | + CalculateSmallVectorDefaultInlinedElements<T>::value> |
| 169 | +class [[nodiscard]] JaggedArrayView { |
| 170 | +public: |
| 171 | + using row_type = MutableRowView<T>; |
| 172 | + using container_type = SmallVector<row_type, N>; |
| 173 | + using iterator = typename container_type::iterator; |
| 174 | + using const_iterator = typename container_type::const_iterator; |
| 175 | + |
| 176 | + constexpr JaggedArrayView(MatrixStorage<T, NStorageInline> &Mat, |
| 177 | + size_t RowSpan, size_t ColSpan) |
| 178 | + : Mat(Mat) { |
| 179 | + RowView.reserve(RowSpan); |
| 180 | + for (size_t RowIdx = 0; RowIdx < RowSpan; ++RowIdx) { |
| 181 | + auto RangeBegin = Mat.begin() + RowIdx * ColSpan; |
| 182 | + RowView.emplace_back(RangeBegin, RangeBegin + ColSpan); |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + // Constructor with a full View of the underlying MatrixStorage, if |
| 187 | + // MatrixStorage has a non-zero number of Columns. Otherwise, creates an empty |
| 188 | + // view. |
| 189 | + constexpr JaggedArrayView(MatrixStorage<T, NStorageInline> &Mat) |
| 190 | + : JaggedArrayView(Mat, Mat.getNumRows(), Mat.getNumCols()) {} |
| 191 | + |
| 192 | + // Obvious copy-construator is deleted, since the underlying storage could |
| 193 | + // have changed. |
| 194 | + constexpr JaggedArrayView(const JaggedArrayView &) = delete; |
| 195 | + |
| 196 | + // Copy-assignment operator should not be used when the underlying storage |
| 197 | + // changes. |
| 198 | + constexpr JaggedArrayView &operator=(const JaggedArrayView &Other) { |
| 199 | + assert(Mat.begin() == Other.Mat.begin() && |
| 200 | + "Underlying storage has changed: use custom copy-constructor"); |
| 201 | + RowView = Other.RowView; |
| 202 | + return *this; |
| 203 | + } |
| 204 | + |
| 205 | + // The actual copy-constructor: to be used when the underlying storage is |
| 206 | + // copy-constructed. |
| 207 | + JaggedArrayView(const JaggedArrayView &OldView, |
| 208 | + MatrixStorage<T, NStorageInline> &NewMat) |
| 209 | + : Mat(NewMat) { |
| 210 | + assert(OldView.Mat.size() == Mat.size() && |
| 211 | + "Custom copy-constructor called on non-copied storage"); |
| 212 | + |
| 213 | + // The underlying storage will change. Construct a new RowView by performing |
| 214 | + // pointer-arithmetic on the underlying storage of OldView, using pointers |
| 215 | + // from OldVie. |
| 216 | + for (const auto &R : OldView.RowView) { |
| 217 | + auto [StorageIdx, StartOffset] = OldView.Mat.idxFromRow(R.data()); |
| 218 | + RowView.emplace_back(Mat.rowFromIdx(StorageIdx, StartOffset), R.size()); |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + void addRow(const SmallVectorImpl<T> &Row) { |
| 223 | + // The underlying storage may be resized, performing reallocations. The |
| 224 | + // pointers in RowView will no longer be valid, so save and restore the |
| 225 | + // data. Construct RestoreData by performing pointer-arithmetic on the |
| 226 | + // underlying storgge. |
| 227 | + SmallVector<std::tuple<size_t, size_t, size_t>> RestoreData; |
| 228 | + RestoreData.reserve(RowView.size()); |
| 229 | + for (const auto &R : RowView) { |
| 230 | + auto [StorageIdx, StartOffset] = Mat.idxFromRow(R.data()); |
| 231 | + RestoreData.emplace_back(StorageIdx, StartOffset, R.size()); |
| 232 | + } |
| 233 | + |
| 234 | + Mat.addRow(Row); |
| 235 | + |
| 236 | + // Restore the RowView by performing pointer-arithmetic on the |
| 237 | + // possibly-reallocated storage, using information from RestoreData. |
| 238 | + RowView.clear(); |
| 239 | + for (const auto &[StorageIdx, StartOffset, Len] : RestoreData) |
| 240 | + RowView.emplace_back(Mat.rowFromIdx(StorageIdx, StartOffset), Len); |
| 241 | + |
| 242 | + // Finally, add the new row to the VRowView. |
| 243 | + RowView.emplace_back(Mat.rowFromIdx(Mat.getNumRows() - 1), Row.size()); |
| 244 | + } |
| 245 | + |
| 246 | + // To support addRow(View[Idx]). |
| 247 | + void addRow(const row_type &Row) { addRow(SmallVector<T>{Row}); } |
| 248 | + |
| 249 | + void addRow(std::initializer_list<T> Row) { addRow(SmallVector<T>{Row}); } |
| 250 | + |
| 251 | + constexpr row_type &operator[](size_t RowIdx) { |
| 252 | + assert(RowIdx < RowView.size() && "Indexing out of bounds"); |
| 253 | + return RowView[RowIdx]; |
| 254 | + } |
| 255 | + |
| 256 | + constexpr T *data() const { |
| 257 | + assert(!empty() && "Non-empty view expected"); |
| 258 | + return RowView.front().data(); |
| 259 | + } |
| 260 | + size_t size() const { return getRowSpan() * getMaxColSpan(); } |
| 261 | + bool empty() const { return RowView.empty(); } |
| 262 | + size_t getRowSpan() const { return RowView.size(); } |
| 263 | + size_t getColSpan(size_t RowIdx) const { |
| 264 | + assert(RowIdx < RowView.size() && "Indexing out of bounds"); |
| 265 | + return RowView[RowIdx].size(); |
| 266 | + } |
| 267 | + constexpr size_t getMaxColSpan() const { |
| 268 | + return std::max_element(RowView.begin(), RowView.end(), |
| 269 | + [](const row_type &RowA, const row_type &RowB) { |
| 270 | + return RowA.size() < RowB.size(); |
| 271 | + }) |
| 272 | + ->size(); |
| 273 | + } |
| 274 | + |
| 275 | + iterator begin() { return RowView.begin(); } |
| 276 | + iterator end() { return RowView.end(); } |
| 277 | + const_iterator begin() const { return RowView.begin(); } |
| 278 | + const_iterator end() const { return RowView.end(); } |
| 279 | + |
| 280 | + constexpr JaggedArrayView<T, N, NStorageInline> rowSlice(size_t Begin, |
| 281 | + size_t End) { |
| 282 | + assert(Begin < getRowSpan() && End <= getRowSpan() && |
| 283 | + "Indexing out of bounds"); |
| 284 | + assert(Begin < End && "Invalid slice"); |
| 285 | + container_type NewRowView; |
| 286 | + for (size_t RowIdx = Begin; RowIdx < End; ++RowIdx) |
| 287 | + NewRowView.emplace_back(RowView[RowIdx]); |
| 288 | + return {Mat, std::move(NewRowView)}; |
| 289 | + } |
| 290 | + |
| 291 | + constexpr JaggedArrayView<T, N, NStorageInline> colSlice(size_t Begin, |
| 292 | + size_t End) { |
| 293 | + assert(Begin < End && "Invalid slice"); |
| 294 | + size_t MinColSpan = |
| 295 | + std::min_element(RowView.begin(), RowView.end(), |
| 296 | + [](const row_type &RowA, const row_type &RowB) { |
| 297 | + return RowA.size() < RowB.size(); |
| 298 | + }) |
| 299 | + ->size(); |
| 300 | + assert(Begin < MinColSpan && End <= MinColSpan && "Indexing out of bounds"); |
| 301 | + container_type NewRowView; |
| 302 | + for (row_type Row : RowView) |
| 303 | + NewRowView.emplace_back(Row.slice(Begin, End)); |
| 304 | + return {Mat, std::move(NewRowView)}; |
| 305 | + } |
| 306 | + |
| 307 | + row_type &lastRow() { |
| 308 | + assert(!empty() && "Non-empty view expected"); |
| 309 | + return RowView.back(); |
| 310 | + } |
| 311 | + const row_type &lastRow() const { |
| 312 | + assert(!empty() && "Non-empty view expected"); |
| 313 | + return RowView.back(); |
| 314 | + } |
| 315 | + void dropLastRow() { |
| 316 | + assert(!empty() && "Non-empty view expected"); |
| 317 | + RowView.pop_back(); |
| 318 | + } |
| 319 | + |
| 320 | +protected: |
| 321 | + // Helper constructor. |
| 322 | + constexpr JaggedArrayView(MatrixStorage<T, NStorageInline> &Mat, |
| 323 | + SmallVectorImpl<row_type> &&RowView) |
| 324 | + : Mat(Mat), RowView(std::move(RowView)) {} |
| 325 | + |
| 326 | +private: |
| 327 | + MatrixStorage<T, NStorageInline> &Mat; |
| 328 | + container_type RowView; |
| 329 | +}; |
| 330 | +} // namespace llvm |
| 331 | + |
| 332 | +namespace std { |
| 333 | +template <typename T> |
| 334 | +inline void swap(llvm::MutableRowView<T> &LHS, llvm::MutableRowView<T> &RHS) { |
| 335 | + LHS.swap(RHS); |
| 336 | +} |
| 337 | +} // end namespace std |
| 338 | + |
| 339 | +#endif |
0 commit comments