// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #ifndef MFEM_ARRAY #define MFEM_ARRAY #include "../config/config.hpp" #include "mem_manager.hpp" #include "device.hpp" #include "error.hpp" #include "globals.hpp" #include #include #include #include namespace mfem { template class Array; template void Swap(Array &, Array &); /** Abstract data type Array. Array is an automatically increasing array containing elements of the generic type T. The allocated size may be larger then the logical size of the array. The elements can be accessed by the [] operator, the range is 0 to size-1. */ template class Array { protected: /// Pointer to data Memory data; /// Size of the array int size; inline void GrowSize(int minsize); public: friend void Swap(Array &, Array &); /// Creates an empty array inline Array() : size(0) { data.Reset(); } /// Creates an empty array with a given MemoryType inline Array(MemoryType mt) : size(0) { data.Reset(mt); } /// Creates array of @a asize elements explicit inline Array(int asize) : size(asize) { asize > 0 ? data.New(asize) : data.Reset(); } /** @brief Creates array using an existing c-array of asize elements; allocsize is set to -asize to indicate that the data will not be deleted. */ inline Array(T *_data, int asize) { data.Wrap(_data, asize, false); size = asize; } /// Copy constructor: deep copy from @a src /** This method supports source arrays using any MemoryType. */ inline Array(const Array &src); /// Copy constructor (deep copy) from 'src', an Array of convertible type. template inline Array(const Array &src); /// Destructor inline ~Array() { data.Delete(); } /// Assignment operator: deep copy from 'src'. Array &operator=(const Array &src) { src.Copy(*this); return *this; } /// Assignment operator (deep copy) from @a src, an Array of convertible type. template inline Array &operator=(const Array &src); /// Return the data as 'T *' inline operator T *() { return data; } /// Return the data as 'const T *' inline operator const T *() const { return data; } /// Returns the data inline T *GetData() { return data; } /// Returns the data inline const T *GetData() const { return data; } /// Return a reference to the Memory object used by the Array. Memory &GetMemory() { return data; } /// Return a reference to the Memory object used by the Array, const version. const Memory &GetMemory() const { return data; } /// Return the device flag of the Memory object used by the Array bool UseDevice() const { return data.UseDevice(); } /// Return true if the data will be deleted by the array inline bool OwnsData() const { return data.OwnsHostPtr(); } /// Changes the ownership of the data inline void StealData(T **p) { *p = data; data.Reset(); size = 0; } /// NULL-ifies the data inline void LoseData() { data.Reset(); size = 0; } /// Make the Array own the data void MakeDataOwner() const { data.SetHostPtrOwner(true); } /// Return the logical size of the array. inline int Size() const { return size; } /// Change the logical size of the array, keep existing entries. inline void SetSize(int nsize); /// Same as SetSize(int) plus initialize new entries with 'initval'. inline void SetSize(int nsize, const T &initval); /** @brief Resize the array to size @a nsize using MemoryType @a mt. Note that unlike the other versions of SetSize(), the current content of the array is not preserved. */ inline void SetSize(int nsize, MemoryType mt); /** Maximum number of entries the array can store without allocating more memory. */ inline int Capacity() const { return data.Capacity(); } /// Ensures that the allocated size is at least the given size. inline void Reserve(int capacity) { if (capacity > Capacity()) { GrowSize(capacity); } } /// Reference access to the ith element. inline T & operator[](int i); /// Const reference access to the ith element. inline const T &operator[](int i) const; /// Append element 'el' to array, resize if necessary. inline int Append(const T & el); /// Append another array to this array, resize if necessary. inline int Append(const T *els, int nels); /// Append another array to this array, resize if necessary. inline int Append(const Array &els) { return Append(els, els.Size()); } /// Prepend an 'el' to the array, resize if necessary. inline int Prepend(const T &el); /// Return the last element in the array. inline T &Last(); /// Return the last element in the array. inline const T &Last() const; /// Append element when it is not yet in the array, return index. inline int Union(const T & el); /// Return the first index where 'el' is found; return -1 if not found. inline int Find(const T &el) const; /// Do bisection search for 'el' in a sorted array; return -1 if not found. inline int FindSorted(const T &el) const; /// Delete the last entry of the array. inline void DeleteLast() { if (size > 0) { size--; } } /// Delete the first entry with value == 'el'. inline void DeleteFirst(const T &el); /// Delete the whole array. inline void DeleteAll(); /// Create a copy of the internal array to the provided @a copy. inline void Copy(Array ©) const; /// Make this Array a reference to a pointer. inline void MakeRef(T *, int); /// Make this Array a reference to 'master'. inline void MakeRef(const Array &master); /// Copy sub array starting from @a offset out to the provided @a sa. inline void GetSubArray(int offset, int sa_size, Array &sa) const; /// Prints array to stream with width elements per row. void Print(std::ostream &out = mfem::out, int width = 4) const; /** @brief Save the Array to the stream @a out using the format @a fmt. The format @a fmt can be: 0 - write the size followed by all entries 1 - write only the entries */ void Save(std::ostream &out, int fmt = 0) const; /** @brief Read an Array from the stream @a in using format @a fmt. The format @a fmt can be: 0 - read the size then the entries 1 - read Size() entries */ void Load(std::istream &in, int fmt = 0); /** @brief Set the Array size to @a new_size and read that many entries from the stream @a in. */ void Load(int new_size, std::istream &in) { SetSize(new_size); Load(in, 1); } /** @brief Find the maximal element in the array, using the comparison operator `<` for class T. */ T Max() const; /** @brief Find the minimal element in the array, using the comparison operator `<` for class T. */ T Min() const; /// Sorts the array in ascending order. This requires operator< to be defined for T. void Sort() { std::sort((T*)data, data + size); } /// Sorts the array in ascending order using the supplied comparison function object. template void Sort(Compare cmp) { std::sort((T*)data, data + size, cmp); } /** @brief Removes duplicities from a sorted array. This requires operator== to be defined for T. */ void Unique() { T* end = std::unique((T*)data, data + size); SetSize(end - data); } /// Return 1 if the array is sorted from lowest to highest. Otherwise return 0. int IsSorted(); /// Fill the entries of the array with the cumulative sum of the entries. void PartialSum(); /// Return the sum of all the array entries using the '+'' operator for class 'T'. T Sum(); /// Set all entries of the array to the provided constant. inline void operator=(const T &a); /// Copy data from a pointer. 'Size()' elements are copied. inline void Assign(const T *); /// STL-like copyTo @a dest from begin to end. template inline void CopyTo(U *dest) { std::copy(begin(), end(), dest); } /** @brief Copy from @a src into this array. Copies enough entries to fill the Capacity size of this array. Careful this does not update the Size to match this Capacity after this.*/ template inline void CopyFrom(const U *src) { std::memcpy(begin(), src, MemoryUsage()); } /// STL-like begin. Returns pointer to the first element of the array. inline T* begin() { return data; } /// STL-like end. Returns pointer after the last element of the array. inline T* end() { return data + size; } /// STL-like begin. Returns const pointer to the first element of the array. inline const T* begin() const { return data; } /// STL-like end. Returns const pointer after the last element of the array. inline const T* end() const { return data + size; } /// Returns the number of bytes allocated for the array including any reserve. long MemoryUsage() const { return Capacity() * sizeof(T); } /// Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev). const T *Read(bool on_dev = true) const { return mfem::Read(data, size, on_dev); } /// Shortcut for mfem::Read(a.GetMemory(), a.Size(), false). const T *HostRead() const { return mfem::Read(data, size, false); } /// Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev). T *Write(bool on_dev = true) { return mfem::Write(data, size, on_dev); } /// Shortcut for mfem::Write(a.GetMemory(), a.Size(), false). T *HostWrite() { return mfem::Write(data, size, false); } /// Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), on_dev). T *ReadWrite(bool on_dev = true) { return mfem::ReadWrite(data, size, on_dev); } /// Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false). T *HostReadWrite() { return mfem::ReadWrite(data, size, false); } }; template inline bool operator==(const Array &LHS, const Array &RHS) { if ( LHS.Size() != RHS.Size() ) { return false; } for (int i=0; i inline bool operator!=(const Array &LHS, const Array &RHS) { return !( LHS == RHS ); } template class Array2D; template void Swap(Array2D &, Array2D &); /// Dynamic 2D array using row-major layout template class Array2D { private: friend void Swap(Array2D &, Array2D &); Array array1d; int M, N; // number of rows and columns public: Array2D() { M = N = 0; } Array2D(int m, int n) : array1d(m*n) { M = m; N = n; } void SetSize(int m, int n) { array1d.SetSize(m*n); M = m; N = n; } int NumRows() const { return M; } int NumCols() const { return N; } inline const T &operator()(int i, int j) const; inline T &operator()(int i, int j); inline const T *operator[](int i) const; inline T *operator[](int i); const T *operator()(int i) const { return (*this)[i]; } T *operator()(int i) { return (*this)[i]; } const T *GetRow(int i) const { return (*this)[i]; } T *GetRow(int i) { return (*this)[i]; } /// Extract a copy of the @a i-th row into the Array @a sa. void GetRow(int i, Array &sa) const { sa.SetSize(N); sa.Assign(GetRow(i)); } /** @brief Save the Array2D to the stream @a out using the format @a fmt. The format @a fmt can be: 0 - write the number of rows and columns, followed by all entries 1 - write only the entries, using row-major layout */ void Save(std::ostream &out, int fmt = 0) const { if (fmt == 0) { out << NumRows() << ' ' << NumCols() << '\n'; } array1d.Save(out, 1); } /** @brief Read an Array2D from the stream @a in using format @a fmt. The format @a fmt can be: 0 - read the number of rows and columns, then the entries 1 - read NumRows() x NumCols() entries, using row-major layout */ void Load(std::istream &in, int fmt = 0) { if (fmt == 0) { in >> M >> N; array1d.SetSize(M*N); } array1d.Load(in, 1); } /// Read an Array2D from a file void Load(const char *filename, int fmt = 0); /** @brief Set the Array2D dimensions to @a new_size0 x @a new_size1 and read that many entries from the stream @a in. */ void Load(int new_size0,int new_size1, std::istream &in) { SetSize(new_size0,new_size1); Load(in, 1); } void Copy(Array2D ©) const { copy.M = M; copy.N = N; array1d.Copy(copy.array1d); } inline void operator=(const T &a) { array1d = a; } /// Make this Array a reference to 'master' inline void MakeRef(const Array2D &master) { M = master.M; N = master.N; array1d.MakeRef(master.array1d); } /// Delete all dynamically allocated memory, resetting all dimensions to zero. inline void DeleteAll() { M = 0; N = 0; array1d.DeleteAll(); } /// Prints array to stream with width elements per row void Print(std::ostream &out = mfem::out, int width = 4); }; template class Array3D { private: Array array1d; int N2, N3; public: Array3D() { N2 = N3 = 0; } Array3D(int n1, int n2, int n3) : array1d(n1*n2*n3) { N2 = n2; N3 = n3; } void SetSize(int n1, int n2, int n3) { array1d.SetSize(n1*n2*n3); N2 = n2; N3 = n3; } inline const T &operator()(int i, int j, int k) const; inline T &operator()(int i, int j, int k); }; /** A container for items of type T. Dynamically grows as items are added. * Each item is accessible by its index. Items are allocated in larger chunks * (blocks), so the 'Append' method is very fast on average. */ template class BlockArray { public: BlockArray(int block_size = 16*1024); BlockArray(const BlockArray &other); // deep copy ~BlockArray() { Destroy(); } /// Allocate and construct a new item in the array, return its index. int Append(); /// Allocate and copy-construct a new item in the array, return its index. int Append(const T &item); /// Access item of the array. inline T& At(int index) { CheckIndex(index); return blocks[index >> shift][index & mask]; } inline const T& At(int index) const { CheckIndex(index); return blocks[index >> shift][index & mask]; } /// Access item of the array. inline T& operator[](int index) { return At(index); } inline const T& operator[](int index) const { return At(index); } /// Return the number of items actually stored. int Size() const { return size; } /// Return the current capacity of the BlockArray. int Capacity() const { return blocks.Size()*(mask+1); } /// Destroy all items, set size to zero. void DeleteAll() { Destroy(); blocks.DeleteAll(); size = 0; } void Swap(BlockArray &other); long MemoryUsage() const; protected: template class iterator_base { public: cT& operator*() const { return *ptr; } cT* operator->() const { return ptr; } bool good() const { return !stop; } int index() const { return (ptr - ref); } protected: cA *array; cT *ptr, *b_end, *ref; int b_end_idx; bool stop; iterator_base() { } iterator_base(bool stop) : stop(stop) { } iterator_base(cA *a) : array(a), ptr(a->blocks[0]), ref(ptr), stop(false) { b_end_idx = std::min(a->size, a->mask+1); b_end = ptr + b_end_idx; } void next() { MFEM_ASSERT(!stop, "invalid use"); if (++ptr == b_end) { if (b_end_idx < array->size) { ptr = &array->At(b_end_idx); ref = ptr - b_end_idx; b_end_idx = std::min(array->size, (b_end_idx|array->mask) + 1); b_end = &array->At(b_end_idx-1) + 1; } else { MFEM_ASSERT(b_end_idx == array->size, "invalid use"); stop = true; } } } }; public: class iterator : public iterator_base { protected: friend class BlockArray; typedef iterator_base base; iterator() { } iterator(bool stop) : base(stop) { } iterator(BlockArray *a) : base(a) { } public: iterator &operator++() { base::next(); return *this; } bool operator==(const iterator &other) const { return base::stop; } bool operator!=(const iterator &other) const { return !base::stop; } }; class const_iterator : public iterator_base { protected: friend class BlockArray; typedef iterator_base base; const_iterator() { } const_iterator(bool stop) : base(stop) { } const_iterator(const BlockArray *a) : base(a) { } public: const_iterator &operator++() { base::next(); return *this; } bool operator==(const const_iterator &other) const { return base::stop; } bool operator!=(const const_iterator &other) const { return !base::stop; } }; iterator begin() { return size ? iterator(this) : iterator(true); } iterator end() { return iterator(); } const_iterator cbegin() const { return size ? const_iterator(this) : const_iterator(true); } const_iterator cend() const { return const_iterator(); } protected: Array blocks; int size, shift, mask; int Alloc(); inline void CheckIndex(int index) const { MFEM_ASSERT(index >= 0 && index < size, "Out of bounds access: " << index << ", size = " << size); } void Destroy(); }; /// inlines /// template inline void Swap(T &a, T &b) { T c = a; a = b; b = c; } template inline void Swap(Array &a, Array &b) { Swap(a.data, b.data); Swap(a.size, b.size); } template inline Array::Array(const Array &src) : size(src.Size()) { size > 0 ? data.New(size, src.data.GetMemoryType()) : data.Reset(); data.CopyFrom(src.data, size); data.UseDevice(src.data.UseDevice()); } template template inline Array::Array(const Array &src) : size(src.Size()) { size > 0 ? data.New(size) : data.Reset(); for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); } } template inline void Array::GrowSize(int minsize) { const int nsize = std::max(minsize, 2 * data.Capacity()); Memory p(nsize, data.GetMemoryType()); p.CopyFrom(data, size); p.UseDevice(data.UseDevice()); data.Delete(); data = p; } template template inline Array &Array::operator=(const Array &src) { SetSize(src.Size()); for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); } return *this; } template inline void Array::SetSize(int nsize) { MFEM_ASSERT( nsize>=0, "Size must be non-negative. It is " << nsize ); if (nsize > Capacity()) { GrowSize(nsize); } size = nsize; } template inline void Array::SetSize(int nsize, const T &initval) { MFEM_ASSERT( nsize>=0, "Size must be non-negative. It is " << nsize ); if (nsize > size) { if (nsize > Capacity()) { GrowSize(nsize); } for (int i = size; i < nsize; i++) { data[i] = initval; } } size = nsize; } template inline void Array::SetSize(int nsize, MemoryType mt) { MFEM_ASSERT(nsize >= 0, "invalid new size: " << nsize); if (mt == data.GetMemoryType()) { if (nsize <= Capacity()) { size = nsize; return; } } const bool use_dev = data.UseDevice(); data.Delete(); if (nsize > 0) { data.New(nsize, mt); size = nsize; } else { data.Reset(); size = 0; } data.UseDevice(use_dev); } template inline T &Array::operator[](int i) { MFEM_ASSERT( i>=0 && i inline const T &Array::Last() const { MFEM_ASSERT(size > 0, "Array size is zero: " << size); return data[size-1]; } template inline int Array::Union(const T &el) { int i = 0; while ((i < size) && (data[i] != el)) { i++; } if (i == size) { Append(el); } return i; } template inline int Array::Find(const T &el) const { for (int i = 0; i < size; i++) { if (data[i] == el) { return i; } } return -1; } template inline int Array::FindSorted(const T &el) const { const T *begin = data, *end = begin + size; const T* first = std::lower_bound(begin, end, el); if (first == end || !(*first == el)) { return -1; } return first - begin; } template inline void Array::DeleteFirst(const T &el) { for (int i = 0; i < size; i++) { if (data[i] == el) { for (i++; i < size; i++) { data[i-1] = data[i]; } size--; return; } } } template inline void Array::DeleteAll() { const bool use_dev = data.UseDevice(); data.Delete(); data.Reset(); size = 0; data.UseDevice(use_dev); } template inline void Array::Copy(Array ©) const { copy.SetSize(Size(), data.GetMemoryType()); data.CopyTo(copy.data, Size()); copy.data.UseDevice(data.UseDevice()); } template inline void Array::MakeRef(T *p, int s) { data.Delete(); data.Wrap(p, s, false); size = s; } template inline void Array::MakeRef(const Array &master) { data.Delete(); data = master.data; // note: copies the device flag size = master.size; data.ClearOwnerFlags(); } template inline void Array::GetSubArray(int offset, int sa_size, Array &sa) const { sa.SetSize(sa_size); for (int i = 0; i < sa_size; i++) { sa[i] = (*this)[offset+i]; } } template inline void Array::operator=(const T &a) { for (int i = 0; i < size; i++) { data[i] = a; } } template inline void Array::Assign(const T *p) { data.CopyFromHost(p, Size()); } template inline const T &Array2D::operator()(int i, int j) const { MFEM_ASSERT( i>=0 && i< array1d.Size()/N && j>=0 && j inline T &Array2D::operator()(int i, int j) { MFEM_ASSERT( i>=0 && i< array1d.Size()/N && j>=0 && j inline const T *Array2D::operator[](int i) const { MFEM_ASSERT( i>=0 && i< array1d.Size()/N, "Array2D: invalid access of row " << i << " in array with " << array1d.Size()/N << " rows."); return &array1d[i*N]; } template inline T *Array2D::operator[](int i) { MFEM_ASSERT( i>=0 && i< array1d.Size()/N, "Array2D: invalid access of row " << i << " in array with " << array1d.Size()/N << " rows."); return &array1d[i*N]; } template inline void Swap(Array2D &a, Array2D &b) { Swap(a.array1d, b.array1d); Swap(a.N, b.N); } template inline const T &Array3D::operator()(int i, int j, int k) const { MFEM_ASSERT(i >= 0 && i < array1d.Size() / N2 / N3 && j >= 0 && j < N2 && k >= 0 && k < N3, "Array3D: invalid access of element (" << i << ',' << j << ',' << k << ") in array of size (" << array1d.Size() / N2 / N3 << ',' << N2 << ',' << N3 << ")."); return array1d[(i*N2+j)*N3+k]; } template inline T &Array3D::operator()(int i, int j, int k) { MFEM_ASSERT(i >= 0 && i < array1d.Size() / N2 / N3 && j >= 0 && j < N2 && k >= 0 && k < N3, "Array3D: invalid access of element (" << i << ',' << j << ',' << k << ") in array of size (" << array1d.Size() / N2 / N3 << ',' << N2 << ',' << N3 << ")."); return array1d[(i*N2+j)*N3+k]; } template BlockArray::BlockArray(int block_size) { mask = block_size-1; MFEM_VERIFY(!(block_size & mask), "block_size must be a power of two."); size = shift = 0; while ((1 << shift) < block_size) { shift++; } } template BlockArray::BlockArray(const BlockArray &other) { blocks.SetSize(other.blocks.Size()); size = other.size; shift = other.shift; mask = other.mask; int bsize = mask+1; for (int i = 0; i < blocks.Size(); i++) { blocks[i] = (T*) new char[bsize * sizeof(T)]; } // copy all items for (int i = 0; i < size; i++) { new (&At(i)) T(other[i]); } } template int BlockArray::Alloc() { int bsize = mask+1; if (size >= blocks.Size() * bsize) { T* new_block = (T*) new char[bsize * sizeof(T)]; blocks.Append(new_block); } return size++; } template int BlockArray::Append() { int index = Alloc(); new (&At(index)) T(); return index; } template int BlockArray::Append(const T &item) { int index = Alloc(); new (&At(index)) T(item); return index; } template void BlockArray::Swap(BlockArray &other) { mfem::Swap(blocks, other.blocks); std::swap(size, other.size); std::swap(shift, other.shift); std::swap(mask, other.mask); } template long BlockArray::MemoryUsage() const { return blocks.Size()*(mask+1)*sizeof(T) + blocks.MemoryUsage(); } template void BlockArray::Destroy() { int bsize = size & mask; for (int i = blocks.Size(); i != 0; ) { T *block = blocks[--i]; for (int j = bsize; j != 0; ) { block[--j].~T(); } delete [] (char*) block; bsize = mask+1; } } } // namespace mfem #endif