Newer
Older
mbed-os / platform / cxxsupport / mstd_span
@Marc Emmers Marc Emmers on 16 Jan 2021 8 KB Adds basic span implementation
/* mbed Microcontroller Library
 * Copyright (c) 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 MSTD_SPAN_
#define MSTD_SPAN_

#include <mstd_iterator>
#include <mstd_type_traits>

namespace mstd {

inline constexpr size_t dynamic_extent = -1;

namespace detail {
template<typename ElementType, size_t Extent>
class storage {
public:
    constexpr storage() noexcept = default;

    constexpr storage(ElementType *ptr, size_t) noexcept :
        _data(ptr)
    {}

    ElementType* _data;
    static constexpr size_t _size = Extent;
};

template<typename ElementType>
class storage<ElementType, dynamic_extent> {
public:
    constexpr storage() noexcept = default;

    constexpr storage(ElementType *ptr, size_t size) noexcept :
        _data(ptr), _size(size)
    {}

    ElementType* _data;
    size_t _size;
};
} // namespace detail

template<typename ElementType, size_t Extent = dynamic_extent>
class span {
public:
    using element_type = ElementType;
    using value_type = typename mstd::remove_cv_t<element_type>;
    using index_type = size_t;
    using difference_type = ptrdiff_t;
    using pointer = element_type*;
    using const_pointer = const element_type*;
    using reference = element_type&;
    using const_reference = const element_type&;
    using iterator = pointer;
    using reverse_iterator = std::reverse_iterator<iterator>;

    static constexpr index_type extent = Extent;

    // Constructors, copy and assignment
    template<size_t E = Extent,
        typename mstd::enable_if_t<E == dynamic_extent, int> = 0>
    constexpr span() noexcept
    {}

    constexpr span(pointer ptr, index_type count) :
        _storage(ptr, count)
    {
        MBED_ASSERT(extent == dynamic_extent || extent == count);
    }

    constexpr span(pointer first, pointer last) :
        _storage(first, last - first)
    {
        MBED_ASSERT(first <= last);
        MBED_ASSERT(extent == dynamic_extent || extent == last - first);
        MBED_ASSERT(extent == 0 || nullptr != first);
    }

    template<size_t N, size_t E = Extent,
    typename mstd::enable_if_t<E == dynamic_extent || N == E, int> = 0>
    constexpr span(element_type (&arr)[N]) noexcept:
        _storage(arr, N)
    {}

    template<size_t N, size_t E = Extent,
    typename mstd::enable_if_t<E == dynamic_extent || N == E, int> = 0>
        constexpr span(array<value_type, N>& arr) noexcept:
        _storage(arr.data(), N)
    {}

    template<size_t N, size_t E = Extent,
    typename mstd::enable_if_t<E == dynamic_extent || N == E, int> = 0>
        constexpr span(const array<value_type, N>& arr) noexcept:
        _storage(arr.data(), N)
    {}

    /* TODO
    template<class R>
    constexpr explicit(extent != dynamic_extent) span(R&& r)
    */

    constexpr span(const span& other) noexcept = default;

    template<class OtherElementType, size_t OtherExtent,
    typename mstd::enable_if_t<(Extent == dynamic_extent || OtherExtent == Extent) &&
        is_convertible<OtherElementType(*)[], ElementType (*)[]>::value, int> = 0>
    constexpr span(const span<OtherElementType, OtherExtent>& s) noexcept:
        _storage(s.data(), s.size())
    {}

    ~span() noexcept = default;

    constexpr span& operator=(const span& other) noexcept = default;

    // Subviews
    template<size_t Count>
    constexpr span<element_type, Count> first() const
    {
        static_assert(Count <= extent);
        MBED_ASSERT(Count <= size());
        return span<element_type, Count>(data(), Count);
    }

    template<size_t Count>
    constexpr span<element_type, Count> last() const
    {
        static_assert(Count <= extent);
        MBED_ASSERT(Count <= size());
        return span<element_type, Count>(data() + (size() - Count), Count);
    }

    template<size_t Offset, size_t Count = dynamic_extent>
    constexpr span<element_type, Count != dynamic_extent ? Count
        : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)> subspan() const
    {
        static_assert(Offset <= extent && (Count == dynamic_extent || Count <= extent - Offset));
        MBED_ASSERT(Offset <= size() && (Count == dynamic_extent || Count <= size() - Offset));
        return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
    }

    constexpr span<element_type, dynamic_extent> first(index_type count) const
    {
        MBED_ASSERT(count <= size());
        return span<element_type>(data(), count);
    }

    constexpr span<element_type, dynamic_extent> last(index_type count) const
    {
        MBED_ASSERT(count <= size());
        return span<element_type>(data() + (size() - count), count);
    }

    constexpr span<element_type, dynamic_extent>
        subspan(index_type offset, index_type count = dynamic_extent) const
    {
        MBED_ASSERT(offset <= size() && (count == dynamic_extent || count <= size() - offset ));
        return span<element_type, dynamic_extent>(data() + offset, count == dynamic_extent ? size() - offset : count );
    }

    // Observers
    constexpr index_type size() const noexcept
    {
        return _storage._size;
    }

    constexpr index_type size_bytes() const noexcept
    {
        return size() * sizeof(element_type);
    }

    constexpr bool empty() const noexcept
    {
        return size() == 0;
    }

    // Element access
    constexpr reference operator[](index_type idx) const
    {
        MBED_ASSERT(idx < size());
        return *(data() + idx);
    }

    constexpr reference front() const
    {
        MBED_ASSERT(not empty());
        return *data();
    }

    constexpr reference back() const
    {
        MBED_ASSERT(not empty());
        return *(data() + (size() - 1));
    }

    constexpr pointer data() const noexcept
    {
        return _storage._data;
    }

    // Iterators
    constexpr iterator begin() const noexcept
    {
        return data();
    }

    constexpr iterator end() const noexcept
    {
        return data() + size();
    }

    constexpr reverse_iterator rbegin() const noexcept
    {
        return reverse_iterator(end());
    }

    constexpr reverse_iterator rend() const noexcept
    {
        return reverse_iterator(begin());
    }

private:
    detail::storage<element_type, extent> _storage;
};

template<class ElementType, size_t Extent>
span<const unsigned char, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
as_bytes(span<ElementType, Extent> s) noexcept
{
    return {reinterpret_cast<const unsigned char*>(s.data()), s.size_bytes()};
}

template<class ElementType, size_t Extent>
span<unsigned char, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
as_writable_bytes(span<ElementType, Extent> s) noexcept
{
    static_assert(not is_const<ElementType>::value);
    return {reinterpret_cast<unsigned char*>(s.data()), s.size_bytes()};
}

template<class ElementType, size_t Extent>
constexpr span<ElementType, Extent> make_span(ElementType (&arr)[Extent])
{
    return span<ElementType, Extent>(arr);
}

template<class ElementType, size_t Extent>
constexpr span<const ElementType, Extent> make_span(const ElementType (&arr)[Extent])
{
    return span<const ElementType, Extent>(arr);
}

template<class ElementType, size_t Extent>
constexpr span<ElementType, Extent> make_span(array<ElementType, Extent>& arr)
{
    return span<ElementType, Extent>(arr);
}

template<class ElementType, size_t Extent>
constexpr span<const ElementType, Extent> make_span(const array<ElementType, Extent>& arr)
{
    return span<const ElementType, Extent>(arr);
}

/* TODO
template<class R>
constexpr span<typename Container::value_type> make_span(R&& cont)
{
    return {cont};
}
*/

} // namespace mstd

#endif // MSTD_MUTEX_