/* mbed Microcontroller Library * Copyright (c) 2018-2019 ARM Limited * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MBED_PLATFORM_SPAN_H_ #define MBED_PLATFORM_SPAN_H_ #include #include #include #include "platform/mbed_assert.h" namespace mbed { /** \ingroup mbed-os-public */ /** \addtogroup platform-public-api */ /** @{*/ /** * \defgroup platform_Span Span class * @{ */ // Internal details of Span // It is used construct Span from Span of convertible types (non const -> const) namespace span_detail { // If From type is convertible to To type, then the compilation constant value is // true; otherwise, it is false. template class is_convertible { struct true_type { char x[512]; }; struct false_type { }; static const From &generator(); static true_type sink(const To &); static false_type sink(...); public: static const bool value = sizeof(true_type) == sizeof(sink(generator())); }; } #if defined(DOXYGEN_ONLY) /** * Special value for the Extent parameter of Span. * If the type uses this value, then the size of the array is stored in the object * at runtime. * * @relates Span */ const ptrdiff_t SPAN_DYNAMIC_EXTENT = -1; #else #define SPAN_DYNAMIC_EXTENT -1 #endif /** * Nonowning view to a sequence of contiguous elements. * * Spans encapsulate a pointer to a sequence of contiguous elements and its size * into a single object. Span can replace the traditional pair of pointer and * size arguments passed as array definitions in function calls. * * @par Operations * * Span objects can be copied and assigned like regular value types with the help * of the copy constructor or the copy assignment (=) operator. * * You can retrieve elements of the object with the subscript ([]) operator. You can access the * pointer to the first element of the sequence viewed with data(). * The function size() returns the number of elements in the sequence, and * empty() informs whether there is any element in the sequence. * * You can slice Span from the beginning of the sequence (first()), from the end * of the sequence (last()) or from an arbitrary point of the sequence (subspan()). * * @par Size encoding * * The size of the sequence can be encoded in the type itself or in the value of * the instance with the help of the template parameter Extent: * * - Span: Span over a sequence of 6 elements. * - Span: Span over an arbitrary long sequence. * * When the size is encoded in the type itself, it is guaranteed that the Span * view is a valid sequence (not empty() and not NULL) - unless Extent equals 0. * The type system also prevents automatic conversion from Span of different * sizes. Finally, the Span object is internally represented as a single pointer. * * When the size of the sequence viewed is encoded in the Span value, Span * instances can view an empty sequence. The function empty() helps client code * decide whether Span is viewing valid content or not. * * @par Example * * - Encoding fixed size array: Array values in parameter decays automatically * to pointer, which leaves room for subtitle bugs: * * @code typedef uint8_t mac_address_t[6]; void process_mac(mac_address_t); // compile just fine uint8_t *invalid_value = NULL; process_mac(invalid_value); // correct way typedef Span mac_address_t; void process_mac(mac_address_t); // compilation error uint8_t *invalid_value = NULL; process_mac(invalid_value); // compilation ok uint8_t valid_value[6]; process_mac(valid_value); * @endcode * * - Arbitrary buffer: When dealing with multiple buffers, it becomes painful to * keep track of every buffer size and pointer. * * @code const uint8_t options_tag[OPTIONS_TAG_SIZE]; struct parsed_value_t { uint8_t *header; uint8_t *options; uint8_t *payload; size_t payload_size; } parsed_value_t parse(uint8_t *buffer, size_t buffer_size) { parsed_value_t parsed_value { 0 }; if (buffer != NULL && buffer_size <= MINIMAL_BUFFER_SIZE) { return parsed_value; } parsed_value.header = buffer; parsed_value.header_size = BUFFER_HEADER_SIZE; if (memcmp(buffer + HEADER_OPTIONS_INDEX, options_tag, sizeof(options_tag)) == 0) { options = buffer + BUFFER_HEADER_SIZE; payload = buffer + BUFFER_HEADER_SIZE + OPTIONS_SIZE; payload_size = buffer_size - BUFFER_HEADER_SIZE + OPTIONS_SIZE; } else { payload = buffer + BUFFER_HEADER_SIZE; payload_size = buffer_size - BUFFER_HEADER_SIZE; } return parsed_value; } //with Span struct parsed_value_t { Span header; Span options; Span payload; } parsed_value_t parse(const Span &buffer) { parsed_value_t parsed_value; if (buffer.size() <= MINIMAL_BUFFER_SIZE) { return parsed_value; } parsed_value.header = buffer.first(BUFFER_HEADER_SIZE); if (buffer.subspan() == option_tag) { options = buffer.supspan(parsed_value.header.size(), OPTIONS_SIZE); } payload = buffer.subspan(parsed_value.header.size() + parsed_value.options.size()); return parsed_value; } * @endcode * * @note You can create Span instances with the help of the function template * make_Span() and make_const_Span(). * * @note Span objects can be implicitly converted to Span objects * where required. * * @tparam ElementType type of objects the Span views. * * @tparam Extent The size of the contiguous sequence viewed. The default value * SPAN_DYNAMIC_SIZE is special because it allows construction of Span objects of * any size (set at runtime). */ template struct Span { /** * Type of the element contained */ typedef ElementType element_type; /** * Type of the index. */ typedef ptrdiff_t index_type; /** * Pointer to an ElementType */ typedef element_type *pointer; /** * Reference to an ElementType */ typedef element_type &reference; /** * Size of the Extent; -1 if dynamic. */ static const index_type extent = Extent; MBED_STATIC_ASSERT(Extent >= 0, "Invalid extent for a Span"); /** * Construct an empty Span. * * @post a call to size() returns 0, and data() returns NULL. * * @note This function is not accessible if Extent != SPAN_DYNAMIC_EXTENT or * Extent != 0 . */ Span() : _data(NULL) { MBED_STATIC_ASSERT( Extent == 0, "Cannot default construct a static-extent Span (unless Extent is 0)" ); } /** * Construct a Span from a pointer to a buffer and its size. * * @param ptr Pointer to the beginning of the data viewed. * * @param count Number of elements viewed. * * @pre [ptr, ptr + count) must be be a valid range. * @pre count must be equal to Extent. * * @post a call to size() returns Extent, and data() returns @p ptr. */ Span(pointer ptr, index_type count) : _data(ptr) { MBED_ASSERT(count == Extent); MBED_ASSERT(Extent == 0 || ptr != NULL); } /** * Construct a Span from the range [first, last). * * @param first Pointer to the beginning of the data viewed. * @param last End of the range (element after the last element). * * @pre [first, last) must be be a valid range. * @pre first <= last. * @pre last - first must be equal to Extent. * * @post a call to size() returns Extent, and data() returns @p first. */ Span(pointer first, pointer last) : _data(first) { MBED_ASSERT(first <= last); MBED_ASSERT((last - first) == Extent); MBED_ASSERT(Extent == 0 || first != NULL); } // AStyle ignore, not handling correctly below // *INDENT-OFF* /** * Construct a Span from the reference to an array. * * @param elements Reference to the array viewed. * * @post a call to size() returns Extent, and data() returns a * pointer to elements. */ Span(element_type (&elements)[Extent]): _data(elements) { } /** * Construct a Span object from another Span of the same size. * * @param other The Span object used to construct this. * * @note For Span with a positive extent, this function is not accessible. * * @note OtherElementType(*)[] must be convertible to ElementType(*)[]. */ template Span(const Span &other): _data(other.data()) { MBED_STATIC_ASSERT( (span_detail::is_convertible::value), "OtherElementType(*)[] should be convertible to ElementType (*)[]" ); } // *INDENT-ON* /** * Return the size of the sequence viewed. * * @return The size of the sequence viewed. */ index_type size() const { return Extent; } /** * Return if the sequence is empty or not. * * @return true if the sequence is empty and false otherwise. */ bool empty() const { return size() == 0; } /** * Returns a reference to the element at position @p index. * * @param index Index of the element to access. * * @return A reference to the element at the index specified in input. * * @pre 0 <= index < Extent. */ reference operator[](index_type index) const { #ifdef MBED_DEBUG MBED_ASSERT(0 <= index && index < Extent); #endif return _data[index]; } /** * Return a pointer to the first element of the sequence or NULL if the Span * is empty(). * * @return The pointer to the first element of the Span. */ pointer data() const { return _data; } /** * Create a new Span over the first @p Count elements of the existing view. * * @tparam Count The number of element viewed by the new Span * * @return A new Span over the first @p Count elements. * * @pre Count >= 0 && Count <= size(). */ template Span first() const { MBED_STATIC_ASSERT( (0 <= Count) && (Count <= Extent), "Invalid subspan extent" ); return Span(_data, Count); } /** * Create a new Span over the last @p Count elements of the existing view. * * @tparam Count The number of element viewed by the new Span. * * @return A new Span over the last @p Count elements. * * @pre Count >= 0 && Count <= size(). */ template Span last() const { MBED_STATIC_ASSERT( (0 <= Count) && (Count <= Extent), "Invalid subspan extent" ); return Span(_data + (Extent - Count), Count); } // AStyle ignore, not handling correctly below // *INDENT-OFF* /** * Create a subspan that is a view of other Count elements; the view starts at * element Offset. * * @tparam Offset The offset of the first element viewed by the subspan. * * @tparam Count The number of elements present in the subspan. If Count * is equal to SPAN_DYNAMIC_EXTENT, then a Span starting at offset and * containing the rest of the elements is returned. * * @return A subspan of this starting at Offset and Count long. */ template Span subspan() const { MBED_STATIC_ASSERT( 0 <= Offset && Offset <= Extent, "Invalid subspan offset" ); MBED_STATIC_ASSERT( (Count == SPAN_DYNAMIC_EXTENT) || (0 <= Count && (Count + Offset) <= Extent), "Invalid subspan count" ); return Span( _data + Offset, Count == SPAN_DYNAMIC_EXTENT ? Extent - Offset : Count ); } // *INDENT-ON* /** * Create a new Span over the first @p count elements of the existing view. * * @param count The number of element viewed by the new Span. * * @return A new Span over the first @p count elements. */ Span first(index_type count) const { MBED_ASSERT(0 <= count && count <= Extent); return Span(_data, count); } /** * Create a new Span over the last @p count elements of the existing view. * * @param count The number of elements viewed by the new Span. * * @return A new Span over the last @p count elements. */ Span last(index_type count) const { MBED_ASSERT(0 <= count && count <= Extent); return Span( _data + (Extent - count), count ); } /** * Create a subspan that is a view of other count elements; the view starts at * element offset. * * @param offset The offset of the first element viewed by the subspan. * * @param count The number of elements present in the subspan. If Count * is equal to SPAN_DYNAMIC_EXTENT, then a span starting at offset and * containing the rest of the elements is returned. * * @return */ Span subspan( index_type offset, index_type count = SPAN_DYNAMIC_EXTENT ) const { MBED_ASSERT(0 <= offset && offset <= Extent); MBED_ASSERT( (count == SPAN_DYNAMIC_EXTENT) || (0 <= count && (count + offset) <= Extent) ); return Span( _data + offset, count == SPAN_DYNAMIC_EXTENT ? Extent - offset : count ); } private: pointer _data; }; /** * Span specialization that handle dynamic size. */ template struct Span { /** * Type of the element contained. */ typedef ElementType element_type; /** * Type of the index. */ typedef ptrdiff_t index_type; /** * Pointer to an ElementType. */ typedef element_type *pointer; /** * Reference to an ElementType. */ typedef element_type &reference; /** * Size of the Extent; -1 if dynamic. */ static const index_type extent = SPAN_DYNAMIC_EXTENT; /** * Construct an empty Span. * * @post a call to size() returns 0, and data() returns NULL. * * @note This function is not accessible if Extent != SPAN_DYNAMIC_EXTENT or * Extent != 0 . */ Span() : _data(NULL), _size(0) { } /** * Construct a Span from a pointer to a buffer and its size. * * @param ptr Pointer to the beginning of the data viewed. * * @param count Number of elements viewed. * * @pre [ptr, ptr + count) must be be a valid range. * @pre count must be equal to extent. * * @post a call to size() returns count, and data() returns @p ptr. */ Span(pointer ptr, index_type count) : _data(ptr), _size(count) { MBED_ASSERT(count >= 0); MBED_ASSERT(ptr != NULL || count == 0); } /** * Construct a Span from the range [first, last). * * @param first Pointer to the beginning of the data viewed. * @param last End of the range (element after the last element). * * @pre [first, last) must be be a valid range. * @pre first <= last. * * @post a call to size() returns the result of (last - first), and * data() returns @p first. */ Span(pointer first, pointer last) : _data(first), _size(last - first) { MBED_ASSERT(first <= last); MBED_ASSERT(first != NULL || (last - first) == 0); } // AStyle ignore, not handling correctly below // *INDENT-OFF* /** * Construct a Span from the reference to an array. * * @param elements Reference to the array viewed. * * @tparam Count Number of elements of T presents in the array. * * @post a call to size() returns Count, and data() returns a * pointer to elements. */ template Span(element_type (&elements)[Count]): _data(elements), _size(Count) { } /** * Construct a Span object from another Span. * * @param other The Span object used to construct this. * * @note For Span with a positive extent, this function is not accessible. * * @note OtherElementType(*)[] must be convertible to ElementType(*)[]. */ template Span(const Span &other): _data(other.data()), _size(other.size()) { MBED_STATIC_ASSERT( (span_detail::is_convertible::value), "OtherElementType(*)[] should be convertible to ElementType (*)[]" ); } // *INDENT-ON* /** * Return the size of the array viewed. * * @return The number of elements present in the array viewed. */ index_type size() const { return _size; } /** * Return if the sequence viewed is empty or not. * * @return true if the sequence is empty and false otherwise. */ bool empty() const { return size() == 0; } /** * Access to an element of the sequence. * * @param index Element index to access. * * @return A reference to the element at the index specified in input. * * @pre index is less than size(). */ reference operator[](index_type index) const { #ifdef MBED_DEBUG MBED_ASSERT(0 <= index && index < _size); #endif return _data[index]; } /** * Get the raw pointer to the sequence viewed. * * @return The raw pointer to the first element viewed. */ pointer data() const { return _data; } /** * Create a new Span over the first @p Count elements of the existing view. * * @tparam Count The number of elements viewed by the new Span. * * @return A new Span over the first @p Count elements. * * @pre Count >= 0 && Count <= size(). */ template Span first() const { MBED_ASSERT((Count >= 0) && (Count <= _size)); return Span(_data, Count); } /** * Create a new Span over the last @p Count elements of the existing view. * * @tparam Count The number of elements viewed by the new Span. * * @return A new Span over the last @p Count elements. * * @pre Count >= 0 && Count <= size(). */ template Span last() const { MBED_ASSERT((0 <= Count) && (Count <= _size)); return Span(_data + (_size - Count), Count); } /** * Create a subspan that is a view other Count elements; the view starts at * element Offset. * * @tparam Offset The offset of the first element viewed by the subspan. * * @tparam Count The number of elements present in the subspan. If Count * is equal to SPAN_DYNAMIC_EXTENT, then a Span starting at offset and * containing the rest of the elements is returned. * * @return A subspan of this starting at Offset and Count long. */ template Span subspan() const { MBED_ASSERT(0 <= Offset && Offset <= _size); MBED_ASSERT( (Count == SPAN_DYNAMIC_EXTENT) || (0 <= Count && (Count + Offset) <= _size) ); return Span( _data + Offset, Count == SPAN_DYNAMIC_EXTENT ? _size - Offset : Count ); } /** * Create a new Span over the first @p count elements of the existing view. * * @param count The number of elements viewed by the new Span. * * @return A new Span over the first @p count elements. */ Span first(index_type count) const { MBED_ASSERT(0 <= count && count <= _size); return Span(_data, count); } /** * Create a new Span over the last @p count elements of the existing view. * * @param count The number of elements viewed by the new Span. * * @return A new Span over the last @p count elements. */ Span last(index_type count) const { MBED_ASSERT(0 <= count && count <= _size); return Span( _data + (_size - count), count ); } /** * Create a subspan that is a view of other count elements; the view starts at * element offset. * * @param offset The offset of the first element viewed by the subspan. * * @param count The number of elements present in the subspan. If Count * is equal to SPAN_DYNAMIC_EXTENT, then a Span starting at offset and * containing the rest of the elements is returned. * * @return A subspan of this starting at offset and count long. */ Span subspan( index_type offset, index_type count = SPAN_DYNAMIC_EXTENT ) const { MBED_ASSERT(0 <= offset && offset <= _size); MBED_ASSERT( (count == SPAN_DYNAMIC_EXTENT) || (0 <= count && (count + offset) <= _size) ); return Span( _data + offset, count == SPAN_DYNAMIC_EXTENT ? _size - offset : count ); } private: pointer _data; index_type _size; }; /** * Equality operator between two Span objects. * * @param lhs Left side of the binary operation. * @param rhs Right side of the binary operation. * * @return True if Spans in input have the same size and the same content and * false otherwise. * * @relates Span */ template bool operator==(const Span &lhs, const Span &rhs) { if (lhs.size() != rhs.size()) { return false; } if (lhs.data() == rhs.data()) { return true; } return std::equal(lhs.data(), lhs.data() + lhs.size(), rhs.data()); } // AStyle ignore, not handling correctly below // *INDENT-OFF* /** * Equality operation between a Span and a reference to a C++ array. * * @param lhs Left side of the binary operation. * @param rhs Right side of the binary operation. * * @return True if elements in input have the same size and the same content and * false otherwise. */ template bool operator==(const Span &lhs, T (&rhs)[RhsExtent]) { return lhs == Span(rhs); } /** * Equality operation between a Span and a reference to a C++ array. * * @param lhs Left side of the binary operation. * @param rhs Right side of the binary operation. * * @return True if elements in input have the same size and the same content * and false otherwise. */ template bool operator==(T (&lhs)[LhsExtent], const Span &rhs) { return Span(lhs) == rhs; } /** * Not equal operator * * @param lhs Left side of the binary operation. * @param rhs Right side of the binary operation. * * @return True if arrays in input do not have the same size or the same content * and false otherwise. * * @relates Span */ template bool operator!=(const Span &lhs, const Span &rhs) { return !(lhs == rhs); } /** * Not Equal operation between a Span and a reference to a C++ array. * * @param lhs Left side of the binary operation. * @param rhs Right side of the binary operation. * * @return True if elements in input have the same size and the same content * and false otherwise. */ template bool operator!=(const Span &lhs, T (&rhs)[RhsExtent]) { return !(lhs == Span(rhs)); } /** * Not Equal operation between a Span and a reference to a C++ array. * * @param lhs Left side of the binary operation. * @param rhs Right side of the binary operation. * * @return True if elements in input have the same size and the same content * and false otherwise. */ template bool operator!=(T (&lhs)[LhsExtent], const Span &rhs) { return !(Span(lhs) == rhs); } /** * Generate a Span from a reference to a C/C++ array. * * @tparam T Type of elements held in elements. * @tparam Extent Number of items held in elements. * * @param elements The reference to the array viewed. * * @return The Span to elements. * * @note This helper avoids the typing of template parameter when Span is * created 'inline'. * * @relates Span */ template Span make_Span(T (&elements)[Size]) { return Span(elements); } /** * Generate a Span from a pointer to a C/C++ array. * * @tparam Extent Number of items held in elements. * @tparam T Type of elements held in elements. * * @param elements The reference to the array viewed. * * @return The Span to elements. * * @note This helper avoids the typing of template parameter when Span is * created 'inline'. */ template Span make_Span(T *elements) { return Span(elements, Extent); } /** * Generate a Span from a C/C++ pointer and the size of the array. * * @tparam T Type of elements held in array_ptr. * * @param array_ptr The pointer to the array viewed. * @param array_size The number of T elements in the array. * * @return The Span to array_ptr with a size of array_size. * * @note This helper avoids the typing of template parameter when Span is * created 'inline'. * * @relates Span */ template Span make_Span(T *array_ptr, ptrdiff_t array_size) { return Span(array_ptr, array_size); } /** * Generate a Span to a const content from a reference to a C/C++ array. * * @tparam T Type of elements held in elements. * @tparam Extent Number of items held in elements. * * @param elements The array viewed. * @return The Span to elements. * * @note This helper avoids the typing of template parameter when Span is * created 'inline'. */ template Span make_const_Span(const T (&elements)[Extent]) { return Span(elements); } // *INDENT-ON* /** * Generate a Span to a const content from a pointer to a C/C++ array. * * @tparam Extent Number of items held in elements. * @tparam T Type of elements held in elements. * * @param elements The reference to the array viewed. * * @return The Span to elements. * * @note This helper avoids the typing of template parameter when Span is * created 'inline'. * * @relates Span */ template Span make_const_Span(const T *elements) { return Span(elements, Extent); } /** * Generate a Span to a const content from a C/C++ pointer and the size of the * array. * * @tparam T Type of elements held in array_ptr. * * @param array_ptr The pointer to the array to viewed. * @param array_size The number of T elements in the array. * * @return The Span to array_ptr with a size of array_size. * * @note This helper avoids the typing of template parameter when Span is * created 'inline'. * * @relates Span */ template Span make_const_Span(T *array_ptr, size_t array_size) { return Span(array_ptr, array_size); } /**@}*/ /**@}*/ } // namespace mbed #endif /* MBED_PLATFORM_SPAN_H_ */