// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once #include #include #include #include namespace Common { // 'ArrayView' is a pair of pointer to constant object of parametrized type and size. // It is recommended to pass 'ArrayView' to procedures by value. // 'ArrayView' supports 'EMPTY' and 'NIL' representations as follows: // 'data' == 'nullptr' && 'size' == 0 - EMPTY NIL // 'data' != 'nullptr' && 'size' == 0 - EMPTY NOTNIL // 'data' == 'nullptr' && 'size' > 0 - Undefined // 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL // For signed integer 'Size', 'ArrayView' with 'size' < 0 is undefined. template class ArrayView { public: typedef Object ObjectType; typedef Size SizeType; const static Size INVALID; const static ArrayView EMPTY; const static ArrayView NIL; // Default constructor. // Leaves object uninitialized. Any usage before initializing it is undefined. ArrayView() #ifndef NDEBUG : data(nullptr), size(INVALID) // In debug mode, fill in object with invalid state (undefined). #endif { } // Direct constructor. // The behavior is undefined unless 'arrayData' != 'nullptr' || 'arraySize' == 0 ArrayView(const Object* arrayData, Size arraySize) : data(arrayData), size(arraySize) { assert(data != nullptr || size == 0); } // Constructor from C array. // The behavior is undefined unless 'arrayData' != 'nullptr' || 'arraySize' == 0. Input state can be malformed using poiner conversions. template ArrayView(const Object(&arrayData)[arraySize]) : data(arrayData), size(arraySize) { assert(data != nullptr || size == 0); } // Copy constructor. // Performs default action - bitwise copying of source object. // The behavior is undefined unless 'other' 'ArrayView' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 ArrayView(const ArrayView& other) : data(other.data), size(other.size) { assert(data != nullptr || size == 0); } // Destructor. // No special action is performed. ~ArrayView() { } // Copy assignment operator. // The behavior is undefined unless 'other' 'ArrayView' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 ArrayView& operator=(const ArrayView& other) { assert(other.data != nullptr || other.size == 0); data = other.data; size = other.size; return *this; } const Object* getData() const { assert(data != nullptr || size == 0); return data; } Size getSize() const { assert(data != nullptr || size == 0); return size; } // Return false if 'ArrayView' is not EMPTY. // The behavior is undefined unless 'ArrayView' was initialized. bool isEmpty() const { assert(data != nullptr || size == 0); return size == 0; } // Return false if 'ArrayView' is not NIL. // The behavior is undefined unless 'ArrayView' was initialized. bool isNil() const { assert(data != nullptr || size == 0); return data == nullptr; } // Get 'ArrayView' element by index. // The behavior is undefined unless 'ArrayView' was initialized and 'index' < 'size'. const Object& operator[](Size index) const { assert(data != nullptr || size == 0); assert(index < size); return *(data + index); } // Get first element. // The behavior is undefined unless 'ArrayView' was initialized and 'size' > 0 const Object& first() const { assert(data != nullptr || size == 0); assert(size > 0); return *data; } // Get last element. // The behavior is undefined unless 'ArrayView' was initialized and 'size' > 0 const Object& last() const { assert(data != nullptr || size == 0); assert(size > 0); return *(data + (size - 1)); } // Return a pointer to the first element. // The behavior is undefined unless 'ArrayView' was initialized. const Object* begin() const { assert(data != nullptr || size == 0); return data; } // Return a pointer after the last element. // The behavior is undefined unless 'ArrayView' was initialized. const Object* end() const { assert(data != nullptr || size == 0); return data + size; } // Compare elements of two arrays, return false if there is a difference. // EMPTY and NIL arrays are considered equal. // The behavior is undefined unless both arrays were initialized. bool operator==(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size == other.size) { for (Size i = 0;; ++i) { if (i == size) { return true; } if (!(*(data + i) == *(other.data + i))) { break; } } } return false; } // Compare elements two arrays, return false if there is no difference. // EMPTY and NIL arrays are considered equal. // The behavior is undefined unless both arrays were initialized. bool operator!=(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size == other.size) { for (Size i = 0;; ++i) { if (i == size) { return false; } if (*(data + i) != *(other.data + i)) { break; } } } return true; } // Return false if 'ArrayView' does not contain 'object' at the beginning. // The behavior is undefined unless 'ArrayView' was initialized. bool beginsWith(const Object& object) const { assert(data != nullptr || size == 0); if (size == 0) { return false; } return *data == object; } // Return false if 'ArrayView' does not contain 'other' at the beginning. // The behavior is undefined unless both arrays were initialized. bool beginsWith(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size >= other.size) { for (Size i = 0;; ++i) { if (i == other.size) { return true; } if (!(*(data + i) == *(other.data + i))) { break; } } } return false; } // Return false if 'ArrayView' does not contain 'object'. // The behavior is undefined unless 'ArrayView' was initialized. bool contains(const Object& object) const { assert(data != nullptr || size == 0); for (Size i = 0; i < size; ++i) { if (*(data + i) == object) { return true; } } return false; } // Return false if 'ArrayView' does not contain 'other'. // The behavior is undefined unless both arrays were initialized. bool contains(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size >= other.size) { Size i = size - other.size; for (Size j = 0; !(i < j); ++j) { for (Size k = 0;; ++k) { if (k == other.size) { return true; } if (!(*(data + j + k) == *(other.data + k))) { break; } } } } return false; } // Return false if 'ArrayView' does not contain 'object' at the end. // The behavior is undefined unless 'ArrayView' was initialized. bool endsWith(const Object& object) const { assert(data != nullptr || size == 0); if (size == 0) { return false; } return *(data + (size - 1)) == object; } // Return false if 'ArrayView' does not contain 'other' at the end. // The behavior is undefined unless both arrays were initialized. bool endsWith(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size >= other.size) { Size i = size - other.size; for (Size j = 0;; ++j) { if (j == other.size) { return true; } if (!(*(data + i + j) == *(other.data + j))) { break; } } } return false; } // Looks for the first occurence of 'object' in 'ArrayView', // returns index or INVALID if there are no occurences. // The behavior is undefined unless 'ArrayView' was initialized. Size find(const Object& object) const { assert(data != nullptr || size == 0); for (Size i = 0; i < size; ++i) { if (*(data + i) == object) { return i; } } return INVALID; } // Looks for the first occurence of 'other' in 'ArrayView', // returns index or INVALID if there are no occurences. // The behavior is undefined unless both arrays were initialized. Size find(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size >= other.size) { Size i = size - other.size; for (Size j = 0; !(i < j); ++j) { for (Size k = 0;; ++k) { if (k == other.size) { return j; } if (!(*(data + j + k) == *(other.data + k))) { break; } } } } return INVALID; } // Looks for the last occurence of 'object' in 'ArrayView', // returns index or INVALID if there are no occurences. // The behavior is undefined unless 'ArrayView' was initialized. Size findLast(const Object& object) const { assert(data != nullptr || size == 0); for (Size i = 0; i < size; ++i) { if (*(data + (size - 1 - i)) == object) { return size - 1 - i; } } return INVALID; } // Looks for the first occurence of 'other' in 'ArrayView', // returns index or INVALID if there are no occurences. // The behavior is undefined unless both arrays were initialized. Size findLast(ArrayView other) const { assert(data != nullptr || size == 0); assert(other.data != nullptr || other.size == 0); if (size >= other.size) { Size i = size - other.size; for (Size j = 0; !(i < j); ++j) { for (Size k = 0;; ++k) { if (k == other.size) { return i - j; } if (!(*(data + (i - j + k)) == *(other.data + k))) { break; } } } } return INVALID; } // Returns subarray of 'headSize' first elements. // The behavior is undefined unless 'ArrayView' was initialized and 'headSize' <= 'size'. ArrayView head(Size headSize) const { assert(data != nullptr || size == 0); assert(headSize <= size); return ArrayView(data, headSize); } // Returns subarray of 'tailSize' last elements. // The behavior is undefined unless 'ArrayView' was initialized and 'tailSize' <= 'size'. ArrayView tail(Size tailSize) const { assert(data != nullptr || size == 0); assert(tailSize <= size); return ArrayView(data + (size - tailSize), tailSize); } // Returns 'ArrayView' without 'headSize' first elements. // The behavior is undefined unless 'ArrayView' was initialized and 'headSize' <= 'size'. ArrayView unhead(Size headSize) const { assert(data != nullptr || size == 0); assert(headSize <= size); return ArrayView(data + headSize, size - headSize); } // Returns 'ArrayView' without 'tailSize' last elements. // The behavior is undefined unless 'ArrayView' was initialized and 'tailSize' <= 'size'. ArrayView untail(Size tailSize) const { assert(data != nullptr || size == 0); assert(tailSize <= size); return ArrayView(data, size - tailSize); } // Returns subarray starting at 'startIndex' and contaning 'endIndex' - 'startIndex' elements. // The behavior is undefined unless 'ArrayView' was initialized and 'startIndex' <= 'endIndex' and 'endIndex' <= 'size'. ArrayView range(Size startIndex, Size endIndex) const { assert(data != nullptr || size == 0); assert(startIndex <= endIndex && endIndex <= size); return ArrayView(data + startIndex, endIndex - startIndex); } // Returns subarray starting at 'startIndex' and contaning 'sliceSize' elements. // The behavior is undefined unless 'ArrayView' was initialized and 'startIndex' <= 'size' and 'startIndex' + 'sliceSize' <= 'size'. ArrayView slice(Size startIndex, Size sliceSize) const { assert(data != nullptr || size == 0); assert(startIndex <= size && startIndex + sliceSize <= size); return ArrayView(data + startIndex, sliceSize); } protected: const Object* data; Size size; }; template const Size ArrayView::INVALID = std::numeric_limits::max(); template const ArrayView ArrayView::EMPTY(reinterpret_cast(1), 0); template const ArrayView ArrayView::NIL(nullptr, 0); }