Bug 1295611 - Add mozilla::Span. r=froydnj,gerv.
authorHenri Sivonen <hsivonen@hsivonen.fi>
Thu, 16 Feb 2017 11:43:50 +0200
changeset 401202 261772d7198542e9cf1f0577a55446785a2c189d
parent 401171 773f4dd4aa0284a14b7c661dc0520991d8101907
child 401203 5fa5043019f2d4c9cc871ec75f0598e07f2eed22
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, gerv
bugs1295611
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1295611 - Add mozilla::Span. r=froydnj,gerv. MozReview-Commit-ID: HGNDClVctbE
mfbt/Casting.h
mfbt/Range.h
mfbt/Span.h
mfbt/moz.build
mfbt/tests/gtest/TestSpan.cpp
mfbt/tests/gtest/moz.build
mfbt/tests/moz.build
toolkit/content/license.html
xpcom/ds/nsTArray.h
xpcom/string/nsTSubstring.h
--- a/mfbt/Casting.h
+++ b/mfbt/Casting.h
@@ -233,11 +233,24 @@ IsInBounds(const From aFrom)
 template<typename To, typename From>
 inline To
 AssertedCast(const From aFrom)
 {
   MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
   return static_cast<To>(aFrom);
 }
 
+/**
+ * Cast a value of integral type |From| to a value of integral type |To|,
+ * release asserting that the cast will be a safe cast per C++ (that is, that
+ * |to| is in the range of values permitted for the type |From|).
+ */
+template<typename To, typename From>
+inline To
+ReleaseAssertedCast(const From aFrom)
+{
+  MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
+  return static_cast<To>(aFrom);
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Casting_h */
--- a/mfbt/Range.h
+++ b/mfbt/Range.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_Range_h
 #define mozilla_Range_h
 
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Span.h"
 
 #include <stddef.h>
 
 namespace mozilla {
 
 // Range<T> is a tuple containing a pointer and a length.
 template <typename T>
 class Range
@@ -39,20 +40,51 @@ public:
   template<typename U,
            class = typename EnableIf<IsConvertible<U (*)[], T (*)[]>::value,
                                      int>::Type>
   MOZ_IMPLICIT Range(const Range<U>& aOther)
     : mStart(aOther.mStart),
       mEnd(aOther.mEnd)
   {}
 
+  MOZ_IMPLICIT Range(Span<T> aSpan)
+    : Range(aSpan.Elements(), aSpan.Length())
+  {
+  }
+
+  template<typename U,
+           class = typename EnableIf<IsConvertible<U (*)[], T (*)[]>::value,
+                                     int>::Type>
+  MOZ_IMPLICIT Range(const Span<U>& aSpan)
+    : Range(aSpan.Elements(), aSpan.Length())
+  {
+  }
+
   RangedPtr<T> begin() const { return mStart; }
   RangedPtr<T> end() const { return mEnd; }
   size_t length() const { return mEnd - mStart; }
 
   T& operator[](size_t aOffset) const { return mStart[aOffset]; }
 
   explicit operator bool() const { return mStart != nullptr; }
+
+  operator Span<T>() { return Span<T>(mStart.get(), length()); }
+
+  operator Span<const T>() const { return Span<T>(mStart.get(), length()); }
 };
 
+template<class T>
+Span<T>
+MakeSpan(Range<T>& aRange)
+{
+  return aRange;
+}
+
+template<class T>
+Span<const T>
+MakeSpan(const Range<T>& aRange)
+{
+  return aRange;
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Range_h */
new file mode 100644
--- /dev/null
+++ b/mfbt/Span.h
@@ -0,0 +1,1041 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Adapted from https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/include/gsl/span
+// and https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/include/gsl/gsl_util
+
+#ifndef mozilla_Span_h
+#define mozilla_Span_h
+
+#include "mozilla/Array.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <iterator>
+
+// Classifications for reasons why constexpr was removed in C++14 to C++11
+// conversion. Once we upgrade compilers, we can try defining each of these
+// to constexpr to restore a category of constexprs at a time.
+#define MOZ_SPAN_ASSERTION_CONSTEXPR
+#define MOZ_SPAN_GCC_CONSTEXPR
+#define MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR
+#define MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN
+#define MOZ_SPAN_NON_CONST_CONSTEXPR
+
+#ifdef _MSC_VER
+#pragma warning(push)
+
+// turn off some warnings that are noisy about our MOZ_RELEASE_ASSERT statements
+#pragma warning(disable : 4127) // conditional expression is constant
+
+// blanket turn off warnings from CppCoreCheck for now
+// so people aren't annoyed by them when running the tool.
+// more targeted suppressions will be added in a future update to the GSL
+#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
+
+#if _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+
+#endif            // _MSC_VER < 1910
+#endif            // _MSC_VER
+
+namespace mozilla {
+
+// Stuff from gsl_util
+
+// narrow_cast(): a searchable way to do narrowing casts of values
+template<class T, class U>
+inline constexpr T
+narrow_cast(U&& u)
+{
+  return static_cast<T>(mozilla::Forward<U>(u));
+}
+
+// end gsl_util
+
+// [views.constants], constants
+// This was -1 in gsl::span, but using size_t for sizes instead of ptrdiff_t
+// and reserving a magic value that realistically doesn't occur in
+// compile-time-constant Span sizes makes things a lot less messy in terms of
+// comparison between signed and unsigned.
+constexpr const size_t dynamic_extent = mozilla::MaxValue<size_t>::value;
+
+template<class ElementType, size_t Extent = dynamic_extent>
+class Span;
+
+// implementation details
+namespace span_details {
+
+// C++14 types that we don't have because we build as C++11.
+template<class T>
+using remove_cv_t = typename mozilla::RemoveCV<T>::Type;
+template<class T>
+using remove_const_t = typename mozilla::RemoveConst<T>::Type;
+template<bool B, class T, class F>
+using conditional_t = typename mozilla::Conditional<B, T, F>::Type;
+template<class T>
+using add_pointer_t = typename mozilla::AddPointer<T>::Type;
+template<bool B, class T = void>
+using enable_if_t = typename mozilla::EnableIf<B, T>::Type;
+
+template<class T>
+struct is_span_oracle : mozilla::FalseType
+{
+};
+
+template<class ElementType, size_t Extent>
+struct is_span_oracle<mozilla::Span<ElementType, Extent>> : mozilla::TrueType
+{
+};
+
+template<class T>
+struct is_span : public is_span_oracle<remove_cv_t<T>>
+{
+};
+
+template<class T>
+struct is_std_array_oracle : mozilla::FalseType
+{
+};
+
+template<class ElementType, size_t Extent>
+struct is_std_array_oracle<std::array<ElementType, Extent>> : mozilla::TrueType
+{
+};
+
+template<class T>
+struct is_std_array : public is_std_array_oracle<remove_cv_t<T>>
+{
+};
+
+template<size_t From, size_t To>
+struct is_allowed_extent_conversion
+  : public mozilla::IntegralConstant<bool,
+                                  From == To ||
+                                    From == mozilla::dynamic_extent ||
+                                    To == mozilla::dynamic_extent>
+{
+};
+
+template<class From, class To>
+struct is_allowed_element_type_conversion
+  : public mozilla::IntegralConstant<bool, mozilla::IsConvertible<From (*)[], To (*)[]>::value>
+{
+};
+
+template<class Span, bool IsConst>
+class span_iterator
+{
+  using element_type_ = typename Span::element_type;
+
+public:
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = remove_const_t<element_type_>;
+  using difference_type = typename Span::index_type;
+
+  using reference = conditional_t<IsConst, const element_type_, element_type_>&;
+  using pointer = add_pointer_t<reference>;
+
+  constexpr span_iterator() : span_iterator(nullptr, 0) {}
+
+  MOZ_SPAN_ASSERTION_CONSTEXPR span_iterator(const Span* span,
+                                             typename Span::index_type index)
+    : span_(span)
+    , index_(index)
+  {
+    MOZ_RELEASE_ASSERT(span == nullptr ||
+                       (index_ >= 0 && index <= span_->Length()));
+  }
+
+  friend class span_iterator<Span, true>;
+  constexpr MOZ_IMPLICIT span_iterator(const span_iterator<Span, false>& other)
+    : span_iterator(other.span_, other.index_)
+  {
+  }
+
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR span_iterator<Span, IsConst>&
+  operator=(const span_iterator<Span, IsConst>&) = default;
+
+  MOZ_SPAN_GCC_CONSTEXPR reference operator*() const
+  {
+    MOZ_RELEASE_ASSERT(span_);
+    return (*span_)[index_];
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR pointer operator->() const
+  {
+    MOZ_RELEASE_ASSERT(span_);
+    return &((*span_)[index_]);
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator& operator++()
+  {
+    MOZ_RELEASE_ASSERT(span_ && index_ >= 0 && index_ < span_->Length());
+    ++index_;
+    return *this;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator operator++(int)
+  {
+    auto ret = *this;
+    ++(*this);
+    return ret;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator& operator--()
+  {
+    MOZ_RELEASE_ASSERT(span_ && index_ > 0 && index_ <= span_->Length());
+    --index_;
+    return *this;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator operator--(int)
+  {
+    auto ret = *this;
+    --(*this);
+    return ret;
+  }
+
+  MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN span_iterator
+  operator+(difference_type n) const
+  {
+    auto ret = *this;
+    return ret += n;
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR span_iterator& operator+=(difference_type n)
+  {
+    MOZ_RELEASE_ASSERT(span_ && (index_ + n) >= 0 &&
+                       (index_ + n) <= span_->Length());
+    index_ += n;
+    return *this;
+  }
+
+  MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN span_iterator
+  operator-(difference_type n) const
+  {
+    auto ret = *this;
+    return ret -= n;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator& operator-=(difference_type n)
+
+  {
+    return *this += -n;
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR difference_type
+  operator-(const span_iterator& rhs) const
+  {
+    MOZ_RELEASE_ASSERT(span_ == rhs.span_);
+    return index_ - rhs.index_;
+  }
+
+  constexpr reference operator[](difference_type n) const
+  {
+    return *(*this + n);
+  }
+
+  constexpr friend bool operator==(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_;
+  }
+
+  constexpr friend bool operator!=(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return !(lhs == rhs);
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR friend bool operator<(const span_iterator& lhs,
+                                               const span_iterator& rhs)
+  {
+    MOZ_RELEASE_ASSERT(lhs.span_ == rhs.span_);
+    return lhs.index_ < rhs.index_;
+  }
+
+  constexpr friend bool operator<=(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return !(rhs < lhs);
+  }
+
+  constexpr friend bool operator>(const span_iterator& lhs,
+                                  const span_iterator& rhs)
+  {
+    return rhs < lhs;
+  }
+
+  constexpr friend bool operator>=(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return !(rhs > lhs);
+  }
+
+  void swap(span_iterator& rhs)
+  {
+    std::swap(index_, rhs.index_);
+    std::swap(span_, rhs.span_);
+  }
+
+protected:
+  const Span* span_;
+  size_t index_;
+};
+
+template<class Span, bool IsConst>
+inline constexpr span_iterator<Span, IsConst>
+operator+(typename span_iterator<Span, IsConst>::difference_type n,
+          const span_iterator<Span, IsConst>& rhs)
+{
+  return rhs + n;
+}
+
+template<size_t Ext>
+class extent_type
+{
+public:
+  using index_type = size_t;
+
+  static_assert(Ext >= 0, "A fixed-size Span must be >= 0 in size.");
+
+  constexpr extent_type() {}
+
+  template<index_type Other>
+  MOZ_SPAN_ASSERTION_CONSTEXPR MOZ_IMPLICIT extent_type(extent_type<Other> ext)
+  {
+    static_assert(
+      Other == Ext || Other == dynamic_extent,
+      "Mismatch between fixed-size extent and size of initializing data.");
+    MOZ_RELEASE_ASSERT(ext.size() == Ext);
+  }
+
+  MOZ_SPAN_ASSERTION_CONSTEXPR MOZ_IMPLICIT extent_type(index_type length)
+  {
+    MOZ_RELEASE_ASSERT(length == Ext);
+  }
+
+  constexpr index_type size() const { return Ext; }
+};
+
+template<>
+class extent_type<dynamic_extent>
+{
+public:
+  using index_type = size_t;
+
+  template<index_type Other>
+  explicit constexpr extent_type(extent_type<Other> ext)
+    : size_(ext.size())
+  {
+  }
+
+  explicit constexpr extent_type(index_type length)
+    : size_(length)
+  {
+  }
+
+  constexpr index_type size() const { return size_; }
+
+private:
+  index_type size_;
+};
+} // namespace span_details
+
+/**
+ * Span - slices for C++
+ *
+ * Span implements Rust's slice concept for C++. It's called "Span" instead of
+ * "Slice" to follow the naming used in C++ Core Guidelines.
+ *
+ * A Span wraps a pointer and a length that identify a non-owning view to a
+ * contiguous block of memory of objects of the same type. Various types,
+ * including (pre-decay) C arrays, XPCOM strings, nsTArray, mozilla::Array,
+ * mozilla::Range and contiguous standard-library containers, auto-convert
+ * into Spans when attempting to pass them as arguments to methods that take
+ * Spans. MakeSpan() functions can be used for explicit conversion in other
+ * contexts. (Span itself autoconverts into mozilla::Range.)
+ *
+ * Like Rust's slices, Span provides safety against out-of-bounds access by
+ * performing run-time bound checks. However, unlike Rust's slices, Span
+ * cannot provide safety against use-after-free.
+ *
+ * (Note: Span is like Rust's slice only conceptually. Due to the lack of
+ * ABI guarantees, you should still decompose spans/slices to raw pointer
+ * and length parts when crossing the FFI.)
+ *
+ * In addition to having constructors and MakeSpan() functions that take
+ * various well-known types, a Span for an arbitrary type can be constructed
+ * (via constructor or MakeSpan()) from a pointer and a length or a pointer
+ * and another pointer pointing just past the last element.
+ *
+ * A Span<const char> can be obtained for const char* pointing to a
+ * zero-terminated C string using the MakeCStringSpan() function. A
+ * corresponding implicit constructor does not exist in order to avoid
+ * accidental construction in cases where const char* does not point to a
+ * zero-terminated C string.
+ *
+ * Span has methods that follow the Mozilla naming style and methods that
+ * don't. The methods that follow the Mozilla naming style are meant to be
+ * used directly from Mozilla code. The methods that don't are meant for
+ * integration with C++11 range-based loops and with meta-programming that
+ * expects the same methods that are found on the standard-library
+ * containers. For example, to decompose a Span into its parts in Mozilla
+ * code, use Elements() and Length() (as with nsTArray) instead of data()
+ * and size() (as with std::vector).
+ *
+ * The pointer and length wrapped by a Span cannot be changed after a Span has
+ * been created. When new values are required, simply create a new Span. Span
+ * has a method called Subspan() that works analogously to the Substring()
+ * method of XPCOM strings taking a start index and an optional length. As a
+ * Mozilla extension (relative to Microsoft's gsl::span that mozilla::Span is
+ * based on), Span has methods From(start), To(end) and FromTo(start, end)
+ * that correspond to Rust's &slice[start..], &slice[..end] and
+ * &slice[start..end], respectively. (That is, the end index is the index of
+ * the first element not to be included in the new subspan.)
+ *
+ * When indicating a Span that's only read from, const goes inside the type
+ * parameter. Don't put const in front of Span. That is:
+ * size_t ReadsFromOneSpanAndWritesToAnother(Span<const uint8_t> aReadFrom,
+ *                                           Span<uint8_t> aWrittenTo);
+ *
+ * Any Span<const T> can be viewed as Span<const uint8_t> using the function
+ * AsBytes(). Any Span<T> can be viewed as Span<uint8_t> using the function
+ * AsWritableBytes().
+ */
+template<class ElementType, size_t Extent>
+class Span
+{
+public:
+  // constants and types
+  using element_type = ElementType;
+  using index_type = size_t;
+  using pointer = element_type*;
+  using reference = element_type&;
+
+  using iterator =
+    span_details::span_iterator<Span<ElementType, Extent>, false>;
+  using const_iterator =
+    span_details::span_iterator<Span<ElementType, Extent>, true>;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  constexpr static const index_type extent = Extent;
+
+  // [Span.cons], Span constructors, copy, assignment, and destructor
+  // "Dependent" is needed to make "span_details::enable_if_t<(Dependent || Extent == 0 || Extent == mozilla::MaxValue<size_t>::value)>" SFINAE,
+  // since "span_details::enable_if_t<(Extent == 0 || Extent == mozilla::MaxValue<size_t>::value)>" is ill-formed when Extent is neither of the extreme values.
+  /**
+   * Constructor with no args.
+   */
+  template<
+    bool Dependent = false,
+    class = span_details::enable_if_t<
+      (Dependent || Extent == 0 || Extent == mozilla::MaxValue<size_t>::value)>>
+  constexpr Span()
+    : storage_(nullptr, span_details::extent_type<0>())
+  {
+  }
+
+  /**
+   * Constructor for nullptr.
+   */
+  constexpr MOZ_IMPLICIT Span(std::nullptr_t) : Span() {}
+
+  /**
+   * Constructor for pointer and length.
+   */
+  constexpr Span(pointer aPtr, index_type aLength)
+    : storage_(aPtr, aLength)
+  {
+  }
+
+  /**
+   * Constructor for start pointer and pointer past end.
+   */
+  constexpr Span(pointer aStartPtr, pointer aEndPtr)
+    : storage_(aStartPtr, std::distance(aStartPtr, aEndPtr))
+  {
+  }
+
+  /**
+   * Constructor for C array.
+   */
+  template<size_t N>
+  constexpr MOZ_IMPLICIT Span(element_type (&aArr)[N])
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for std::array.
+   */
+  template<size_t N,
+           class ArrayElementType = span_details::remove_const_t<element_type>>
+  constexpr MOZ_IMPLICIT Span(std::array<ArrayElementType, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for const std::array.
+   */
+  template<size_t N>
+  constexpr MOZ_IMPLICIT Span(
+    const std::array<span_details::remove_const_t<element_type>, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for mozilla::Array.
+   */
+  template<size_t N,
+           class ArrayElementType = span_details::remove_const_t<element_type>>
+  constexpr MOZ_IMPLICIT Span(mozilla::Array<ArrayElementType, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for const mozilla::Array.
+   */
+  template<size_t N>
+  constexpr MOZ_IMPLICIT Span(
+    const mozilla::Array<span_details::remove_const_t<element_type>, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for mozilla::UniquePtr holding an array and length.
+   */
+  template<class ArrayElementType = std::add_pointer<element_type>>
+  constexpr Span(const mozilla::UniquePtr<ArrayElementType>& aPtr,
+                 index_type aLength)
+    : storage_(aPtr.get(), aLength)
+  {
+  }
+
+  // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement
+  // on Container to be a contiguous sequence container.
+  /**
+   * Constructor for standard-library containers.
+   */
+  template<
+    class Container,
+    class = span_details::enable_if_t<
+      !span_details::is_span<Container>::value &&
+      !span_details::is_std_array<Container>::value &&
+      mozilla::IsConvertible<typename Container::pointer, pointer>::value &&
+      mozilla::IsConvertible<typename Container::pointer,
+                          decltype(mozilla::DeclVal<Container>().data())>::value>>
+  constexpr MOZ_IMPLICIT Span(Container& cont)
+    : Span(cont.data(), ReleaseAssertedCast<index_type>(cont.size()))
+  {
+  }
+
+  /**
+   * Constructor for standard-library containers (const version).
+   */
+  template<
+    class Container,
+    class = span_details::enable_if_t<
+      mozilla::IsConst<element_type>::value &&
+      !span_details::is_span<Container>::value &&
+      mozilla::IsConvertible<typename Container::pointer, pointer>::value &&
+      mozilla::IsConvertible<typename Container::pointer,
+                          decltype(mozilla::DeclVal<Container>().data())>::value>>
+  constexpr MOZ_IMPLICIT Span(const Container& cont)
+    : Span(cont.data(), ReleaseAssertedCast<index_type>(cont.size()))
+  {
+  }
+
+  /**
+   * Constructor from other Span.
+   */
+  constexpr Span(const Span& other) = default;
+
+  /**
+   * Constructor from other Span.
+   */
+  constexpr Span(Span&& other) = default;
+
+  /**
+   * Constructor from other Span with conversion of element type.
+   */
+  template<
+    class OtherElementType,
+    size_t OtherExtent,
+    class = span_details::enable_if_t<
+      span_details::is_allowed_extent_conversion<OtherExtent, Extent>::value &&
+      span_details::is_allowed_element_type_conversion<OtherElementType,
+                                                       element_type>::value>>
+  constexpr MOZ_IMPLICIT Span(const Span<OtherElementType, OtherExtent>& other)
+    : storage_(other.data(),
+               span_details::extent_type<OtherExtent>(other.size()))
+  {
+  }
+
+  /**
+   * Constructor from other Span with conversion of element type.
+   */
+  template<
+    class OtherElementType,
+    size_t OtherExtent,
+    class = span_details::enable_if_t<
+      span_details::is_allowed_extent_conversion<OtherExtent, Extent>::value &&
+      span_details::is_allowed_element_type_conversion<OtherElementType,
+                                                       element_type>::value>>
+  constexpr MOZ_IMPLICIT Span(Span<OtherElementType, OtherExtent>&& other)
+    : storage_(other.data(),
+               span_details::extent_type<OtherExtent>(other.size()))
+  {
+  }
+
+  ~Span() = default;
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR Span& operator=(const Span& other)
+    = default;
+
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR Span& operator=(Span&& other)
+    = default;
+
+  // [Span.sub], Span subviews
+  /**
+   * Subspan with first N elements with compile-time N.
+   */
+  template<size_t Count>
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, Count> First() const
+  {
+    MOZ_RELEASE_ASSERT(Count <= size());
+    return { data(), Count };
+  }
+
+  /**
+   * Subspan with last N elements with compile-time N.
+   */
+  template<size_t Count>
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, Count> Last() const
+  {
+    MOZ_RELEASE_ASSERT(Count <= size());
+    return { data() + (size() - Count), Count };
+  }
+
+  /**
+   * Subspan with compile-time start index and length.
+   */
+  template<size_t Offset, size_t Count = dynamic_extent>
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, Count> Subspan() const
+  {
+    MOZ_RELEASE_ASSERT(Offset <= size() &&
+      (Count == dynamic_extent || (Offset + Count <= size())));
+    return { data() + Offset,
+             Count == dynamic_extent ? size() - Offset : Count };
+  }
+
+  /**
+   * Subspan with first N elements with run-time N.
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> First(
+    index_type aCount) const
+  {
+    MOZ_RELEASE_ASSERT(aCount <= size());
+    return { data(), aCount };
+  }
+
+  /**
+   * Subspan with last N elements with run-time N.
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> Last(
+    index_type aCount) const
+  {
+    MOZ_RELEASE_ASSERT(aCount <= size());
+    return { data() + (size() - aCount), aCount };
+  }
+
+  /**
+   * Subspan with run-time start index and length.
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> Subspan(
+    index_type aStart,
+    index_type aLength = dynamic_extent) const
+  {
+    MOZ_RELEASE_ASSERT(aStart <= size() &&
+                       (aLength == dynamic_extent ||
+                        (aStart + aLength <= size())));
+    return { data() + aStart,
+             aLength == dynamic_extent ? size() - aStart : aLength };
+  }
+
+  /**
+   * Subspan with run-time start index. (Rust's &foo[start..])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> From(
+    index_type aStart) const
+  {
+    return Subspan(aStart);
+  }
+
+  /**
+   * Subspan with run-time exclusive end index. (Rust's &foo[..end])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> To(
+    index_type aEnd) const
+  {
+    return Subspan(0, aEnd);
+  }
+
+  /**
+   * Subspan with run-time start index and exclusive end index.
+   * (Rust's &foo[start..end])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> FromTo(
+    index_type aStart,
+    index_type aEnd) const
+  {
+    MOZ_RELEASE_ASSERT(aStart <= aEnd);
+    return Subspan(aStart, aEnd - aStart);
+  }
+
+  // [Span.obs], Span observers
+  /**
+   * Number of elements in the span.
+   */
+  constexpr index_type Length() const { return size(); }
+
+  /**
+   * Number of elements in the span (standard-libray duck typing version).
+   */
+  constexpr index_type size() const { return storage_.size(); }
+
+  /**
+   * Size of the span in bytes.
+   */
+  constexpr index_type LengthBytes() const { return size_bytes(); }
+
+  /**
+   * Size of the span in bytes (standard-library naming style version).
+   */
+  constexpr index_type size_bytes() const
+  {
+    return size() * narrow_cast<index_type>(sizeof(element_type));
+  }
+
+  /**
+   * Checks if the the length of the span is zero.
+   */
+  constexpr bool IsEmpty() const { return empty(); }
+
+  /**
+   * Checks if the the length of the span is zero (standard-libray duck
+   * typing version).
+   */
+  constexpr bool empty() const { return size() == 0; }
+
+  // [Span.elem], Span element access
+  MOZ_SPAN_GCC_CONSTEXPR reference operator[](index_type idx) const
+  {
+    MOZ_RELEASE_ASSERT(idx < storage_.size());
+    return data()[idx];
+  }
+
+  /**
+   * Access element of span by index (standard-library duck typing version).
+   */
+  constexpr reference at(index_type idx) const { return this->operator[](idx); }
+
+  constexpr reference operator()(index_type idx) const
+  {
+    return this->operator[](idx);
+  }
+
+  /**
+   * Pointer to the first element of the span.
+   */
+  constexpr pointer Elements() const { return data(); }
+
+  /**
+   * Pointer to the first element of the span (standard-libray duck typing version).
+   */
+  constexpr pointer data() const { return storage_.data(); }
+
+  // [Span.iter], Span iterator support
+  iterator begin() const { return { this, 0 }; }
+  iterator end() const { return { this, Length() }; }
+
+  const_iterator cbegin() const { return { this, 0 }; }
+  const_iterator cend() const { return { this, Length() }; }
+
+  reverse_iterator rbegin() const
+  {
+    return reverse_iterator{ end() };
+  }
+  reverse_iterator rend() const
+  {
+    return reverse_iterator{ begin() };
+  }
+
+  const_reverse_iterator crbegin() const
+  {
+    return const_reverse_iterator{ cend() };
+  }
+  const_reverse_iterator crend() const
+  {
+    return const_reverse_iterator{ cbegin() };
+  }
+
+private:
+  // this implementation detail class lets us take advantage of the
+  // empty base class optimization to pay for only storage of a single
+  // pointer in the case of fixed-size Spans
+  template<class ExtentType>
+  class storage_type : public ExtentType
+  {
+  public:
+    template<class OtherExtentType>
+    MOZ_SPAN_ASSERTION_CONSTEXPR storage_type(pointer elements,
+                                              OtherExtentType ext)
+      : ExtentType(ext)
+      , data_(elements)
+    {
+      MOZ_RELEASE_ASSERT(
+        (!elements && ExtentType::size() == 0) ||
+        (elements && ExtentType::size() != mozilla::MaxValue<size_t>::value));
+    }
+
+    constexpr pointer data() const { return data_; }
+
+  private:
+    pointer data_;
+  };
+
+  storage_type<span_details::extent_type<Extent>> storage_;
+};
+
+// [Span.comparison], Span comparison operators
+template<class ElementType, size_t FirstExtent, size_t SecondExtent>
+inline constexpr bool
+operator==(const Span<ElementType, FirstExtent>& l,
+           const Span<ElementType, SecondExtent>& r)
+{
+  return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin());
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator!=(const Span<ElementType, Extent>& l,
+           const Span<ElementType, Extent>& r)
+{
+  return !(l == r);
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator<(const Span<ElementType, Extent>& l,
+          const Span<ElementType, Extent>& r)
+{
+  return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator<=(const Span<ElementType, Extent>& l,
+           const Span<ElementType, Extent>& r)
+{
+  return !(l > r);
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator>(const Span<ElementType, Extent>& l,
+          const Span<ElementType, Extent>& r)
+{
+  return r < l;
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator>=(const Span<ElementType, Extent>& l,
+           const Span<ElementType, Extent>& r)
+{
+  return !(l < r);
+}
+
+namespace span_details {
+// if we only supported compilers with good constexpr support then
+// this pair of classes could collapse down to a constexpr function
+
+// we should use a narrow_cast<> to go to size_t, but older compilers may not see it as
+// constexpr
+// and so will fail compilation of the template
+template<class ElementType, size_t Extent>
+struct calculate_byte_size
+  : mozilla::IntegralConstant<size_t,
+                           static_cast<size_t>(sizeof(ElementType) *
+                                               static_cast<size_t>(Extent))>
+{
+};
+
+template<class ElementType>
+struct calculate_byte_size<ElementType, dynamic_extent>
+  : mozilla::IntegralConstant<size_t, dynamic_extent>
+{
+};
+}
+
+// [Span.objectrep], views of object representation
+/**
+ * View span as Span<const uint8_t>.
+ */
+template<class ElementType, size_t Extent>
+Span<const uint8_t,
+     span_details::calculate_byte_size<ElementType, Extent>::value>
+AsBytes(Span<ElementType, Extent> s)
+{
+  return { reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes() };
+}
+
+/**
+ * View span as Span<uint8_t>.
+ */
+template<class ElementType,
+         size_t Extent,
+         class = span_details::enable_if_t<!mozilla::IsConst<ElementType>::value>>
+Span<uint8_t, span_details::calculate_byte_size<ElementType, Extent>::value>
+AsWritableBytes(Span<ElementType, Extent> s)
+{
+  return { reinterpret_cast<uint8_t*>(s.data()), s.size_bytes() };
+}
+
+//
+// MakeSpan() - Utility functions for creating Spans
+//
+/**
+ * Create span from pointer and length.
+ */
+template<class ElementType>
+Span<ElementType>
+MakeSpan(ElementType* aPtr, typename Span<ElementType>::index_type aLength)
+{
+  return Span<ElementType>(aPtr, aLength);
+}
+
+/**
+ * Create span from start pointer and pointer past end.
+ */
+template<class ElementType>
+Span<ElementType>
+MakeSpan(ElementType* aStartPtr, ElementType* aEndPtr)
+{
+  return Span<ElementType>(aStartPtr, aEndPtr);
+}
+
+/**
+ * Create span from C array.
+ */
+template<class ElementType, size_t N>
+Span<ElementType> MakeSpan(ElementType (&aArr)[N])
+{
+  return Span<ElementType>(aArr);
+}
+
+/**
+ * Create span from mozilla::Array.
+ */
+template<class ElementType, size_t N>
+Span<ElementType>
+MakeSpan(mozilla::Array<ElementType, N>& aArr)
+{
+  return aArr;
+}
+
+/**
+ * Create span from const mozilla::Array.
+ */
+template<class ElementType, size_t N>
+Span<const ElementType>
+MakeSpan(const mozilla::Array<ElementType, N>& arr)
+{
+  return arr;
+}
+
+/**
+ * Create span from standard-library container.
+ */
+template<class Container>
+Span<typename Container::value_type>
+MakeSpan(Container& cont)
+{
+  return Span<typename Container::value_type>(cont);
+}
+
+/**
+ * Create span from standard-library container (const version).
+ */
+template<class Container>
+Span<const typename Container::value_type>
+MakeSpan(const Container& cont)
+{
+  return Span<const typename Container::value_type>(cont);
+}
+
+/**
+ * Create span from smart pointer and length.
+ */
+template<class Ptr>
+Span<typename Ptr::element_type>
+MakeSpan(Ptr& aPtr, size_t aLength)
+{
+  return Span<typename Ptr::element_type>(aPtr, aLength);
+}
+
+/**
+ * Create span from C string.
+ */
+inline Span<const char>
+MakeCStringSpan(const char* aStr)
+{
+  return Span<const char>(aStr, std::strlen(aStr));
+}
+
+} // namespace mozilla
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // _MSC_VER < 1910
+
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#undef MOZ_SPAN_ASSERTION_CONSTEXPR
+#undef MOZ_SPAN_GCC_CONSTEXPR
+#undef MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR
+#undef MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN
+#undef MOZ_SPAN_NON_CONST_CONSTEXPR
+
+#endif // mozilla_Span_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -77,16 +77,17 @@ EXPORTS.mozilla = [
     'ReverseIterator.h',
     'RollingMean.h',
     'Saturate.h',
     'Scoped.h',
     'ScopeExit.h',
     'SegmentedVector.h',
     'SHA1.h',
     'SizePrintfMacros.h',
+    'Span.h',
     'SplayTree.h',
     'Sprintf.h',
     'StaticAnalysisFunctions.h',
     'TaggedAnonymousMemory.h',
     'TemplateLib.h',
     'ThreadLocal.h',
     'ToString.h',
     'Tuple.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/TestSpan.cpp
@@ -0,0 +1,2079 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Adapted from https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/tests/Span_tests.cpp
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Span.h"
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/Range.h"
+#include "mozilla/TypeTraits.h"
+
+#define SPAN_TEST(name) TEST(SpanTest, name)
+#define CHECK_THROW(a, b)
+
+using namespace std;
+using namespace mozilla;
+
+static_assert(IsConvertible<Range<int>, Span<const int>>::value,
+              "Range should convert into const");
+static_assert(IsConvertible<Range<const int>, Span<const int>>::value,
+              "const Range should convert into const");
+static_assert(!IsConvertible<Range<const int>, Span<int>>::value,
+              "Range should not drop const in conversion");
+static_assert(IsConvertible<Span<int>, Range<const int>>::value,
+              "Span should convert into const");
+static_assert(IsConvertible<Span<const int>, Range<const int>>::value,
+              "const Span should convert into const");
+static_assert(!IsConvertible<Span<const int>, Range<int>>::value,
+              "Span should not drop const in conversion");
+static_assert(IsConvertible<Span<const int>, Span<const int>>::value,
+              "const Span should convert into const");
+static_assert(IsConvertible<Span<int>, Span<const int>>::value,
+              "Span should convert into const");
+static_assert(!IsConvertible<Span<const int>, Span<int>>::value,
+              "Span should not drop const in conversion");
+static_assert(IsConvertible<const nsTArray<int>, Span<const int>>::value,
+              "const nsTArray should convert into const");
+static_assert(IsConvertible<nsTArray<int>, Span<const int>>::value,
+              "nsTArray should convert into const");
+static_assert(!IsConvertible<const nsTArray<int>, Span<int>>::value,
+              "nsTArray should not drop const in conversion");
+static_assert(IsConvertible<nsTArray<const int>, Span<const int>>::value,
+              "nsTArray should convert into const");
+static_assert(!IsConvertible<nsTArray<const int>, Span<int>>::value,
+              "nsTArray should not drop const in conversion");
+
+namespace {
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
+}
+
+void
+AssertSpanOfThreeInts(Span<const int> s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 1);
+  ASSERT_EQ(s[1], 2);
+  ASSERT_EQ(s[2], 3);
+}
+
+void
+AssertSpanOfThreeChars(Span<const char> s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 'a');
+  ASSERT_EQ(s[1], 'b');
+  ASSERT_EQ(s[2], 'c');
+}
+
+void
+AssertSpanOfThreeChar16s(Span<const char16_t> s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 'a');
+  ASSERT_EQ(s[1], 'b');
+  ASSERT_EQ(s[2], 'c');
+}
+
+void
+AssertSpanOfThreeCharsViaString(const nsACString& aStr)
+{
+  AssertSpanOfThreeChars(aStr);
+}
+
+void
+AssertSpanOfThreeChar16sViaString(const nsAString& aStr)
+{
+  AssertSpanOfThreeChar16s(aStr);
+}
+
+SPAN_TEST(default_constructor)
+{
+  {
+    Span<int> s;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int, 0> s;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int, 0> cs;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<int, 1> s;
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), nullptr); // explains why it can't compile
+#endif
+  }
+
+  {
+    Span<int> s{};
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs{};
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(size_optimization)
+{
+  {
+    Span<int> s;
+    ASSERT_EQ(sizeof(s), sizeof(int*) + sizeof(size_t));
+  }
+
+  {
+    Span<int, 0> s;
+    ASSERT_EQ(sizeof(s), sizeof(int*));
+  }
+}
+
+SPAN_TEST(from_nullptr_constructor)
+{
+  {
+    Span<int> s = nullptr;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs = nullptr;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int, 0> s = nullptr;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int, 0> cs = nullptr;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<int, 1> s = nullptr;
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), nullptr); // explains why it can't compile
+#endif
+  }
+
+  {
+    Span<int> s{ nullptr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs{ nullptr };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int*> s{ nullptr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int*> cs{ nullptr };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_nullptr_length_constructor)
+{
+  {
+    Span<int> s{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int, 0> s{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int, 0> cs{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+#if 0
+        {
+            auto workaround_macro = []() { Span<int, 1> s{ nullptr, static_cast<Span<int>::index_type>(0) }; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+
+        {
+            auto workaround_macro = []() { Span<int> s{nullptr, 1}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+
+            auto const_workaround_macro = []() { Span<const int> cs{nullptr, 1}; };
+            CHECK_THROW(const_workaround_macro(), fail_fast);
+        }
+
+        {
+            auto workaround_macro = []() { Span<int, 0> s{nullptr, 1}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+
+            auto const_workaround_macro = []() { Span<const int, 0> s{nullptr, 1}; };
+            CHECK_THROW(const_workaround_macro(), fail_fast);
+        }
+#endif
+  {
+    Span<int*> s{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int*> cs{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_pointer_length_constructor)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s{ &arr[0], 2 };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    Span<int, 2> s{ &arr[0], 2 };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    int* p = nullptr;
+    Span<int> s{ p, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+#if 0
+        {
+            int* p = nullptr;
+            auto workaround_macro = [=]() { Span<int> s{p, 2}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+#endif
+
+  {
+    auto s = MakeSpan(&arr[0], 2);
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    int* p = nullptr;
+    auto s = MakeSpan(p, static_cast<Span<int>::index_type>(0));
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+#if 0
+        {
+            int* p = nullptr;
+            auto workaround_macro = [=]() { MakeSpan(p, 2); };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+#endif
+}
+
+SPAN_TEST(from_pointer_pointer_constructor)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s{ &arr[0], &arr[2] };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    Span<int, 2> s{ &arr[0], &arr[2] };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    Span<int> s{ &arr[0], &arr[0] };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 0> s{ &arr[0], &arr[0] };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+  //{
+  //    auto workaround_macro = [&]() { Span<int> s{&arr[1], &arr[0]}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+  //{
+  //    int* p = nullptr;
+  //    auto workaround_macro = [&]() { Span<int> s{&arr[0], p}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  {
+    int* p = nullptr;
+    Span<int> s{ p, p };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+  {
+    int* p = nullptr;
+    Span<int, 0> s{ p, p };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+  // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+  //{
+  //    int* p = nullptr;
+  //    auto workaround_macro = [&]() { Span<int> s{&arr[0], p}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  {
+    auto s = MakeSpan(&arr[0], &arr[2]);
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    auto s = MakeSpan(&arr[0], &arr[0]);
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    int* p = nullptr;
+    auto s = MakeSpan(p, p);
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_array_constructor)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int> s{ arr };
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 5> s{ arr };
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  int arr2d[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 6> s{ arr };
+  }
+
+  {
+    Span<int, 0> s{ arr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<int> s{ arr2d };
+    ASSERT_EQ(s.Length(), 6U);
+    ASSERT_EQ(s.data(), &arr2d[0][0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[5], 6);
+  }
+
+  {
+    Span<int, 0> s{ arr2d };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr2d[0][0]);
+  }
+
+  {
+    Span<int, 6> s{ arr2d };
+  }
+#endif
+  {
+    Span<int[3]> s{ &(arr2d[0]), 1 };
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr2d[0]);
+  }
+
+  int arr3d[2][3][2] = { { { 1, 2 }, { 3, 4 }, { 5, 6 } },
+                         { { 7, 8 }, { 9, 10 }, { 11, 12 } } };
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int> s{ arr3d };
+    ASSERT_EQ(s.Length(), 12U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[11], 12);
+  }
+
+  {
+    Span<int, 0> s{ arr3d };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+  }
+
+  {
+    Span<int, 11> s{ arr3d };
+  }
+
+  {
+    Span<int, 12> s{ arr3d };
+    ASSERT_EQ(s.Length(), 12U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[5], 6);
+  }
+#endif
+  {
+    Span<int[3][2]> s{ &arr3d[0], 1 };
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr3d[0]);
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    auto s = MakeSpan(&(arr2d[0]), 1);
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr2d[0]);
+  }
+
+  {
+    auto s = MakeSpan(&arr3d[0], 1);
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr3d[0]);
+  }
+}
+
+SPAN_TEST(from_dynamic_array_constructor)
+{
+  double(*arr)[3][4] = new double[100][3][4];
+
+  {
+    Span<double> s(&arr[0][0][0], 10);
+    ASSERT_EQ(s.Length(), 10U);
+    ASSERT_EQ(s.data(), &arr[0][0][0]);
+  }
+
+  {
+    auto s = MakeSpan(&arr[0][0][0], 10);
+    ASSERT_EQ(s.Length(), 10U);
+    ASSERT_EQ(s.data(), &arr[0][0][0]);
+  }
+
+  delete[] arr;
+}
+
+SPAN_TEST(from_std_array_constructor)
+{
+  std::array<int, 4> arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span<int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span<int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int, 4> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int, 2> cs{ arr };
+    ASSERT_EQ(cs.size(), 2U);
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span<int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int, 0> cs{ arr };
+    ASSERT_EQ(cs.size(), 0U);
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span<int, 5> s{ arr };
+  }
+
+  {
+    auto get_an_array = []() -> std::array<int, 4> { return { 1, 2, 3, 4 }; };
+    auto take_a_Span = [](Span<int> s) { static_cast<void>(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> std::array<int, 4> {
+      return { { 1, 2, 3, 4 } };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_const_std_array_constructor)
+{
+  const std::array<int, 4> arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> const std::array<int, 4> {
+      return { { 1, 2, 3, 4 } };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_std_array_const_constructor)
+{
+  std::array<const int, 4> arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+
+  {
+    Span<int, 4> s{ arr };
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_mozilla_array_constructor)
+{
+  mozilla::Array<int, 4> arr(1, 2, 3, 4);
+
+  {
+    Span<int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int, 4> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int, 2> cs{ arr };
+    ASSERT_EQ(cs.size(), 2U);
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int, 0> cs{ arr };
+    ASSERT_EQ(cs.size(), 0U);
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 5> s{ arr };
+  }
+
+  {
+    auto get_an_array = []() -> mozilla::Array<int, 4> {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span<int> s) { static_cast<void>(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> mozilla::Array<int, 4> {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_const_mozilla_array_constructor)
+{
+  const mozilla::Array<int, 4> arr(1, 2, 3, 4);
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+#endif
+
+#if 0
+  {
+    auto get_an_array = []() -> const mozilla::Array<int, 4> {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_mozilla_array_const_constructor)
+{
+  mozilla::Array<const int, 4> arr(1, 2, 3, 4);
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+
+  {
+    Span<int, 4> s{ arr };
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_container_constructor)
+{
+  std::vector<int> v = { 1, 2, 3 };
+  const std::vector<int> cv = v;
+
+  {
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size()));
+    ASSERT_EQ(s.data(), v.data());
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.size()));
+    ASSERT_EQ(cs.data(), v.data());
+  }
+
+  std::string str = "hello";
+  const std::string cstr = "hello";
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<char> s{ str };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.size()));
+    ASSERT_EQ(s.data(), str.data());
+#endif
+    Span<const char> cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.size()));
+    ASSERT_EQ(cs.data(), str.data());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<char> s{ cstr };
+#endif
+    Span<const char> cs{ cstr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(cstr.size()));
+    ASSERT_EQ(cs.data(), cstr.data());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_vector = []() -> std::vector<int> { return {}; };
+    auto use_Span = [](Span<int> s) { static_cast<void>(s); };
+    use_Span(get_temp_vector());
+#endif
+  }
+
+  {
+    auto get_temp_vector = []() -> std::vector<int> { return {}; };
+    auto use_Span = [](Span<const int> s) { static_cast<void>(s); };
+    use_Span(get_temp_vector());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_string = []() -> std::string { return {}; };
+    auto use_Span = [](Span<char> s) { static_cast<void>(s); };
+    use_Span(get_temp_string());
+#endif
+  }
+
+  {
+    auto get_temp_string = []() -> std::string { return {}; };
+    auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+    use_Span(get_temp_string());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_vector = []() -> const std::vector<int> { return {}; };
+    auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+    use_Span(get_temp_vector());
+#endif
+  }
+
+  {
+    auto get_temp_string = []() -> const std::string { return {}; };
+    auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+    use_Span(get_temp_string());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    std::map<int, int> m;
+    Span<int> s{ m };
+#endif
+  }
+
+  {
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size()));
+    ASSERT_EQ(s.data(), v.data());
+
+    auto cs = MakeSpan(cv);
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(cv.size()));
+    ASSERT_EQ(cs.data(), cv.data());
+  }
+}
+
+SPAN_TEST(from_xpcom_collections)
+{
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    AutoTArray<int, 5> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    AutoTArray<int, 5> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    FallibleTArray<int> v;
+    *(v.AppendElement(fallible)) = 1;
+    *(v.AppendElement(fallible)) = 2;
+    *(v.AppendElement(fallible)) = 3;
+
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    FallibleTArray<int> v;
+    *(v.AppendElement(fallible)) = 1;
+    *(v.AppendElement(fallible)) = 2;
+    *(v.AppendElement(fallible)) = 3;
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    nsAutoString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChar16s(str);
+    AssertSpanOfThreeChar16sViaString(str);
+
+    Span<char16_t> s{ str };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+
+    Span<const char16_t> cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(cs.data(), str.BeginReading());
+    ASSERT_EQ(cs[2], 'c');
+  }
+  {
+    nsAutoString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChar16s(str);
+    AssertSpanOfThreeChar16sViaString(str);
+
+    auto s = MakeSpan(str);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+  }
+  {
+    nsAutoCString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChars(str);
+    AssertSpanOfThreeCharsViaString(str);
+
+    Span<uint8_t> s{ str };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), reinterpret_cast<uint8_t*>(str.BeginWriting()));
+    ASSERT_EQ(s[2], 'c');
+
+    Span<const uint8_t> cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(cs.data(), reinterpret_cast<const uint8_t*>(str.BeginReading()));
+    ASSERT_EQ(cs[2], 'c');
+  }
+  {
+    nsAutoCString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChars(str);
+    AssertSpanOfThreeCharsViaString(str);
+
+    auto s = MakeSpan(str);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+  }
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    Range<int> r(v.Elements(), v.Length());
+
+    AssertSpanOfThreeInts(r);
+
+    Span<int> s{ r };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ r };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    Range<int> r(v.Elements(), v.Length());
+
+    AssertSpanOfThreeInts(r);
+
+    auto s = MakeSpan(r);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+}
+
+SPAN_TEST(from_cstring)
+{
+  {
+    const char* str = "abc";
+
+    auto cs = MakeCStringSpan(str);
+    ASSERT_EQ(cs.size(), 3U);
+    ASSERT_EQ(cs.data(), str);
+    ASSERT_EQ(cs[2], 'c');
+  }
+}
+
+SPAN_TEST(from_convertible_Span_constructor){
+  {
+    Span<DerivedClass> avd;
+    Span<const DerivedClass> avcd = avd;
+    static_cast<void>(avcd);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+  Span<DerivedClass> avd;
+  Span<BaseClass> avb = avd;
+  static_cast<void>(avb);
+#endif
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int> s;
+    Span<unsigned int> s2 = s;
+    static_cast<void>(s2);
+  }
+
+  {
+    Span<int> s;
+    Span<const unsigned int> s2 = s;
+    static_cast<void>(s2);
+  }
+
+  {
+    Span<int> s;
+    Span<short> s2 = s;
+    static_cast<void>(s2);
+  }
+#endif
+}
+
+SPAN_TEST(copy_move_and_assignment)
+{
+  Span<int> s1;
+  ASSERT_TRUE(s1.empty());
+
+  int arr[] = { 3, 4, 5 };
+
+  Span<const int> s2 = arr;
+  ASSERT_EQ(s2.Length(), 3U);
+  ASSERT_EQ(s2.data(), &arr[0]);
+
+  s2 = s1;
+  ASSERT_TRUE(s2.empty());
+
+  auto get_temp_Span = [&]() -> Span<int> { return { &arr[1], 2 }; };
+  auto use_Span = [&](Span<const int> s) {
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[1]);
+  };
+  use_Span(get_temp_Span());
+
+  s1 = get_temp_Span();
+  ASSERT_EQ(s1.Length(), 2U);
+  ASSERT_EQ(s1.data(), &arr[1]);
+}
+
+SPAN_TEST(first)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.First<2>().Length(), 2U);
+    ASSERT_EQ(av.First(2).Length(), 2U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.First<0>().Length(), 0U);
+    ASSERT_EQ(av.First(0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.First<5>().Length(), 5U);
+    ASSERT_EQ(av.First(5).Length(), 5U);
+  }
+
+#if 0
+        {
+            Span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+            ASSERT_EQ(av.First<6>().Length() , 6U);
+            ASSERT_EQ(av.First<-1>().Length() , -1);
+#endif
+            CHECK_THROW(av.First(6).Length(), fail_fast);
+        }
+#endif
+
+  {
+    Span<int> av;
+    ASSERT_EQ(av.First<0>().Length(), 0U);
+    ASSERT_EQ(av.First(0).Length(), 0U);
+  }
+}
+
+SPAN_TEST(last)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Last<2>().Length(), 2U);
+    ASSERT_EQ(av.Last(2).Length(), 2U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Last<0>().Length(), 0U);
+    ASSERT_EQ(av.Last(0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Last<5>().Length(), 5U);
+    ASSERT_EQ(av.Last(5).Length(), 5U);
+  }
+
+#if 0
+        {
+            Span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+            ASSERT_EQ(av.Last<6>().Length() , 6U);
+#endif
+            CHECK_THROW(av.Last(6).Length(), fail_fast);
+        }
+#endif
+
+  {
+    Span<int> av;
+    ASSERT_EQ(av.Last<0>().Length(), 0U);
+    ASSERT_EQ(av.Last(0).Length(), 0U);
+  }
+}
+
+SPAN_TEST(from_to)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.From(3).Length(), 2U);
+    ASSERT_EQ(av.From(2)[1], 4);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.From(5).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.From(0).Length(), 5U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.To(3).Length(), 3U);
+    ASSERT_EQ(av.To(3)[1], 2);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.To(0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.To(5).Length(), 5U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.FromTo(1, 4).Length(), 3U);
+    ASSERT_EQ(av.FromTo(1, 4)[1], 3);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.FromTo(2, 2).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.FromTo(0, 5).Length(), 5U);
+  }
+}
+
+SPAN_TEST(Subspan)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<2, 2>().Length()), 2U);
+    ASSERT_EQ(av.Subspan(2, 2).Length(), 2U);
+    ASSERT_EQ(av.Subspan(2, 3).Length(), 3U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<0, 5>().Length()), 5U);
+    ASSERT_EQ(av.Subspan(0, 5).Length(), 5U);
+    CHECK_THROW(av.Subspan(0, 6).Length(), fail_fast);
+    CHECK_THROW(av.Subspan(1, 5).Length(), fail_fast);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<4, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(4, 0).Length(), 0U);
+    ASSERT_EQ(av.Subspan(5, 0).Length(), 0U);
+    CHECK_THROW(av.Subspan(6, 0).Length(), fail_fast);
+  }
+
+  {
+    Span<int> av;
+    ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+    CHECK_THROW((av.Subspan<1, 0>().Length()), fail_fast);
+  }
+
+  {
+    Span<int> av;
+    ASSERT_EQ(av.Subspan(0).Length(), 0U);
+    CHECK_THROW(av.Subspan(1).Length(), fail_fast);
+  }
+
+  {
+    Span<int> av = arr;
+    ASSERT_EQ(av.Subspan(0).Length(), 5U);
+    ASSERT_EQ(av.Subspan(1).Length(), 4U);
+    ASSERT_EQ(av.Subspan(4).Length(), 1U);
+    ASSERT_EQ(av.Subspan(5).Length(), 0U);
+    CHECK_THROW(av.Subspan(6).Length(), fail_fast);
+    auto av2 = av.Subspan(1);
+    for (int i = 0; i < 4; ++i)
+      ASSERT_EQ(av2[i], i + 2);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Subspan(0).Length(), 5U);
+    ASSERT_EQ(av.Subspan(1).Length(), 4U);
+    ASSERT_EQ(av.Subspan(4).Length(), 1U);
+    ASSERT_EQ(av.Subspan(5).Length(), 0U);
+    CHECK_THROW(av.Subspan(6).Length(), fail_fast);
+    auto av2 = av.Subspan(1);
+    for (int i = 0; i < 4; ++i)
+      ASSERT_EQ(av2[i], i + 2);
+  }
+}
+
+SPAN_TEST(at_call)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s = arr;
+    ASSERT_EQ(s.at(0), 1);
+    CHECK_THROW(s.at(5), fail_fast);
+  }
+
+  {
+    int arr2d[2] = { 1, 6 };
+    Span<int, 2> s = arr2d;
+    ASSERT_EQ(s.at(0), 1);
+    ASSERT_EQ(s.at(1), 6);
+    CHECK_THROW(s.at(2), fail_fast);
+  }
+}
+
+SPAN_TEST(operator_function_call)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s = arr;
+    ASSERT_EQ(s(0), 1);
+    CHECK_THROW(s(5), fail_fast);
+  }
+
+  {
+    int arr2d[2] = { 1, 6 };
+    Span<int, 2> s = arr2d;
+    ASSERT_EQ(s(0), 1);
+    ASSERT_EQ(s(1), 6);
+    CHECK_THROW(s(2), fail_fast);
+  }
+}
+
+SPAN_TEST(iterator_default_init)
+{
+  Span<int>::iterator it1;
+  Span<int>::iterator it2;
+  ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(const_iterator_default_init)
+{
+  Span<int>::const_iterator it1;
+  Span<int>::const_iterator it2;
+  ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(iterator_conversions)
+{
+  Span<int>::iterator badIt;
+  Span<int>::const_iterator badConstIt;
+  ASSERT_EQ(badIt, badConstIt);
+
+  int a[] = { 1, 2, 3, 4 };
+  Span<int> s = a;
+
+  auto it = s.begin();
+  auto cit = s.cbegin();
+
+  ASSERT_EQ(it, cit);
+  ASSERT_EQ(cit, it);
+
+  Span<int>::const_iterator cit2 = it;
+  ASSERT_EQ(cit2, cit);
+
+  Span<int>::const_iterator cit3 = it + 4;
+  ASSERT_EQ(cit3, s.cend());
+}
+
+SPAN_TEST(iterator_comparisons)
+{
+  int a[] = { 1, 2, 3, 4 };
+  {
+    Span<int> s = a;
+    Span<int>::iterator it = s.begin();
+    auto it2 = it + 1;
+    Span<int>::const_iterator cit = s.cbegin();
+
+    ASSERT_EQ(it, cit);
+    ASSERT_EQ(cit, it);
+    ASSERT_EQ(it, it);
+    ASSERT_EQ(cit, cit);
+    ASSERT_EQ(cit, s.begin());
+    ASSERT_EQ(s.begin(), cit);
+    ASSERT_EQ(s.cbegin(), cit);
+    ASSERT_EQ(it, s.begin());
+    ASSERT_EQ(s.begin(), it);
+
+    ASSERT_NE(it, it2);
+    ASSERT_NE(it2, it);
+    ASSERT_NE(it, s.end());
+    ASSERT_NE(it2, s.end());
+    ASSERT_NE(s.end(), it);
+    ASSERT_NE(it2, cit);
+    ASSERT_NE(cit, it2);
+
+    ASSERT_LT(it, it2);
+    ASSERT_LE(it, it2);
+    ASSERT_LE(it2, s.end());
+    ASSERT_LT(it, s.end());
+    ASSERT_LE(it, cit);
+    ASSERT_LE(cit, it);
+    ASSERT_LT(cit, it2);
+    ASSERT_LE(cit, it2);
+    ASSERT_LT(cit, s.end());
+    ASSERT_LE(cit, s.end());
+
+    ASSERT_GT(it2, it);
+    ASSERT_GE(it2, it);
+    ASSERT_GT(s.end(), it2);
+    ASSERT_GE(s.end(), it2);
+    ASSERT_GT(it2, cit);
+    ASSERT_GE(it2, cit);
+  }
+}
+
+SPAN_TEST(begin_end)
+{
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    Span<int>::iterator it = s.begin();
+    Span<int>::iterator it2 = std::begin(s);
+    ASSERT_EQ(it, it2);
+
+    it = s.end();
+    it2 = std::end(s);
+    ASSERT_EQ(it, it2);
+  }
+
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.begin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 1);
+
+    auto beyond = s.end();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 2);
+    *it = 22;
+    ASSERT_EQ(*it, 22);
+    ASSERT_EQ(beyond - it, 3U);
+
+    it = first;
+    ASSERT_EQ(it, first);
+    while (it != s.end()) {
+      *it = 5;
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+
+    for (auto& n : s) {
+      ASSERT_EQ(n, 5);
+    }
+  }
+}
+
+SPAN_TEST(cbegin_cend)
+{
+#if 0
+          {
+              int a[] = { 1, 2, 3, 4 };
+              Span<int> s = a;
+
+              Span<int>::const_iterator cit = s.cbegin();
+              Span<int>::const_iterator cit2 = std::cbegin(s);
+              ASSERT_EQ(cit , cit2);
+
+              cit = s.cend();
+              cit2 = std::cend(s);
+              ASSERT_EQ(cit , cit2);
+          }
+#endif
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.cbegin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 1);
+
+    auto beyond = s.cend();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 2);
+    ASSERT_EQ(beyond - it, 3U);
+
+    int last = 0;
+    it = first;
+    ASSERT_EQ(it, first);
+    while (it != s.cend()) {
+      ASSERT_EQ(*it, last + 1);
+
+      last = *it;
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+  }
+}
+
+SPAN_TEST(rbegin_rend)
+{
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.rbegin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 4);
+
+    auto beyond = s.rend();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 3);
+    *it = 22;
+    ASSERT_EQ(*it, 22);
+    ASSERT_EQ(beyond - it, 3U);
+
+    it = first;
+    ASSERT_EQ(it, first);
+    while (it != s.rend()) {
+      *it = 5;
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+
+    for (auto& n : s) {
+      ASSERT_EQ(n, 5);
+    }
+  }
+}
+
+SPAN_TEST(crbegin_crend)
+{
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.crbegin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 4);
+
+    auto beyond = s.crend();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 3);
+    ASSERT_EQ(beyond - it, 3U);
+
+    it = first;
+    ASSERT_EQ(it, first);
+    int last = 5;
+    while (it != s.crend()) {
+      ASSERT_EQ(*it, last - 1);
+      last = *it;
+
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+  }
+}
+
+SPAN_TEST(comparison_operators)
+{
+  {
+    Span<int> s1 = nullptr;
+    Span<int> s2 = nullptr;
+    ASSERT_EQ(s1, s2);
+    ASSERT_FALSE(s1 != s2);
+    ASSERT_FALSE(s1 < s2);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s1, s2);
+    ASSERT_EQ(s2, s1);
+    ASSERT_FALSE(s2 != s1);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s2, s1);
+    ASSERT_FALSE(s2 > s1);
+    ASSERT_GE(s2, s1);
+  }
+
+  {
+    int arr[] = { 2, 1 };
+    Span<int> s1 = arr;
+    Span<int> s2 = arr;
+
+    ASSERT_EQ(s1, s2);
+    ASSERT_FALSE(s1 != s2);
+    ASSERT_FALSE(s1 < s2);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s1, s2);
+    ASSERT_EQ(s2, s1);
+    ASSERT_FALSE(s2 != s1);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s2, s1);
+    ASSERT_FALSE(s2 > s1);
+    ASSERT_GE(s2, s1);
+  }
+
+  {
+    int arr[] = { 2, 1 }; // bigger
+
+    Span<int> s1 = nullptr;
+    Span<int> s2 = arr;
+
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_LT(s1, s2);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s2 <= s1);
+    ASSERT_GT(s2, s1);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s2, s1);
+    ASSERT_FALSE(s1 >= s2);
+  }
+
+  {
+    int arr1[] = { 1, 2 };
+    int arr2[] = { 1, 2 };
+    Span<int> s1 = arr1;
+    Span<int> s2 = arr2;
+
+    ASSERT_EQ(s1, s2);
+    ASSERT_FALSE(s1 != s2);
+    ASSERT_FALSE(s1 < s2);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s1, s2);
+    ASSERT_EQ(s2, s1);
+    ASSERT_FALSE(s2 != s1);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s2, s1);
+    ASSERT_FALSE(s2 > s1);
+    ASSERT_GE(s2, s1);
+  }
+
+  {
+    int arr[] = { 1, 2, 3 };
+
+    AssertSpanOfThreeInts(arr);
+
+    Span<int> s1 = { &arr[0], 2 }; // shorter
+    Span<int> s2 = arr;            // longer
+
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_LT(s1, s2);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s2 <= s1);
+    ASSERT_GT(s2, s1);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s2, s1);
+    ASSERT_FALSE(s1 >= s2);
+  }
+
+  {
+    int arr1[] = { 1, 2 }; // smaller
+    int arr2[] = { 2, 1 }; // bigger
+
+    Span<int> s1 = arr1;
+    Span<int> s2 = arr2;
+
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_LT(s1, s2);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s2 <= s1);
+    ASSERT_GT(s2, s1);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s2, s1);
+    ASSERT_FALSE(s1 >= s2);
+  }
+}
+
+SPAN_TEST(as_bytes)
+{
+  int a[] = { 1, 2, 3, 4 };
+
+  {
+    Span<const int> s = a;
+    ASSERT_EQ(s.Length(), 4U);
+    Span<const uint8_t> bs = AsBytes(s);
+    ASSERT_EQ(static_cast<const void*>(bs.data()),
+              static_cast<const void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+
+  {
+    Span<int> s;
+    auto bs = AsBytes(s);
+    ASSERT_EQ(bs.Length(), s.Length());
+    ASSERT_EQ(bs.Length(), 0U);
+    ASSERT_EQ(bs.size_bytes(), 0U);
+    ASSERT_EQ(static_cast<const void*>(bs.data()),
+              static_cast<const void*>(s.data()));
+    ASSERT_EQ(bs.data(), nullptr);
+  }
+
+  {
+    Span<int> s = a;
+    auto bs = AsBytes(s);
+    ASSERT_EQ(static_cast<const void*>(bs.data()),
+              static_cast<const void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+}
+
+SPAN_TEST(as_writable_bytes)
+{
+  int a[] = { 1, 2, 3, 4 };
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    // you should not be able to get writeable bytes for const objects
+    Span<const int> s = a;
+    ASSERT_EQ(s.Length(), 4U);
+    Span<const byte> bs = AsWritableBytes(s);
+    ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+#endif
+  }
+
+  {
+    Span<int> s;
+    auto bs = AsWritableBytes(s);
+    ASSERT_EQ(bs.Length(), s.Length());
+    ASSERT_EQ(bs.Length(), 0U);
+    ASSERT_EQ(bs.size_bytes(), 0U);
+    ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+    ASSERT_EQ(bs.data(), nullptr);
+  }
+
+  {
+    Span<int> s = a;
+    auto bs = AsWritableBytes(s);
+    ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+}
+
+SPAN_TEST(fixed_size_conversions)
+{
+  int arr[] = { 1, 2, 3, 4 };
+
+  // converting to an Span from an equal size array is ok
+  Span<int, 4> s4 = arr;
+  ASSERT_EQ(s4.Length(), 4U);
+
+  // converting to dynamic_range is always ok
+  {
+    Span<int> s = s4;
+    ASSERT_EQ(s.Length(), s4.Length());
+    static_cast<void>(s);
+  }
+
+// initialization or assignment to static Span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 2> s = arr;
+  }
+  {
+    Span<int, 2> s2 = s4;
+    static_cast<void>(s2);
+  }
+#endif
+
+#if 0
+        // even when done dynamically
+        {
+            Span<int> s = arr;
+            auto f = [&]() {
+                Span<int, 2> s2 = s;
+                static_cast<void>(s2);
+            };
+            CHECK_THROW(f(), fail_fast);
+        }
+#endif
+
+  // but doing so explicitly is ok
+
+  // you can convert statically
+  {
+    Span<int, 2> s2 = { arr, 2 };
+    static_cast<void>(s2);
+  }
+  {
+    Span<int, 1> s1 = s4.First<1>();
+    static_cast<void>(s1);
+  }
+
+  // ...or dynamically
+  {
+    // NB: implicit conversion to Span<int,1> from Span<int>
+    Span<int, 1> s1 = s4.First(1);
+    static_cast<void>(s1);
+  }
+
+#if 0
+        // initialization or assignment to static Span that requires size INCREASE is not ok.
+        int arr2[2] = {1, 2};
+#endif
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 4> s3 = arr2;
+  }
+  {
+    Span<int, 2> s2 = arr2;
+    Span<int, 4> s4a = s2;
+  }
+#endif
+
+#if 0
+        {
+            auto f = [&]() {
+                Span<int, 4> _s4 = {arr2, 2};
+                static_cast<void>(_s4);
+            };
+            CHECK_THROW(f(), fail_fast);
+        }
+
+        // this should fail - we are trying to assign a small dynamic Span to a fixed_size larger one
+        Span<int> av = arr2;
+        auto f = [&]() {
+            Span<int, 4> _s4 = av;
+            static_cast<void>(_s4);
+        };
+        CHECK_THROW(f(), fail_fast);
+#endif
+}
+
+#if 0
+    SPAN_TEST(interop_with_std_regex)
+    {
+        char lat[] = { '1', '2', '3', '4', '5', '6', 'E', 'F', 'G' };
+        Span<char> s = lat;
+        auto f_it = s.begin() + 7;
+
+        std::match_results<Span<char>::iterator> match;
+
+        std::regex_match(s.begin(), s.end(), match, std::regex(".*"));
+        ASSERT_EQ(match.ready());
+        ASSERT_TRUE(!match.empty());
+        ASSERT_TRUE(match[0].matched);
+        ASSERT_TRUE(match[0].first , s.begin());
+        ASSERT_EQ(match[0].second , s.end());
+
+        std::regex_search(s.begin(), s.end(), match, std::regex("F"));
+        ASSERT_TRUE(match.ready());
+        ASSERT_TRUE(!match.empty());
+        ASSERT_TRUE(match[0].matched);
+        ASSERT_EQ(match[0].first , f_it);
+        ASSERT_EQ(match[0].second , (f_it + 1));
+    }
+
+SPAN_TEST(interop_with_gsl_at)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+  Span<int> s{ arr };
+  ASSERT_EQ(at(s, 0) , 1 );
+ASSERT_EQ(at(s, 1) , 2U);
+}
+#endif
+
+SPAN_TEST(default_constructible)
+{
+  ASSERT_TRUE((std::is_default_constructible<Span<int>>::value));
+  ASSERT_TRUE((std::is_default_constructible<Span<int, 0>>::value));
+  ASSERT_TRUE((!std::is_default_constructible<Span<int, 42>>::value));
+}
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+    'TestSpan.cpp',
+]
+
+#LOCAL_INCLUDES += [
+#    '../../base',
+#]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -1,14 +1,19 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+if not CONFIG['JS_STANDALONE']:
+    TEST_DIRS += [
+        'gtest',
+    ]
+
 CppUnitTests([
     'TestArray',
     'TestArrayUtils',
     'TestAtomics',
     'TestBinarySearch',
     'TestBloomFilter',
     'TestBufferList',
     'TestCasting',
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -96,16 +96,17 @@
       <li><a href="about:license#hunspell-ee">Estonian Spellchecking Dictionary License</a></li>
       <li><a href="about:license#expat">Expat License</a></li>
       <li><a href="about:license#firebug">Firebug License</a></li>
       <li><a href="about:license#gfx-font-list">gfxFontList License</a></li>
       <li><a href="about:license#google-bsd">Google BSD License</a></li>
       <li><a href="about:license#gears">Google Gears License</a></li>
       <li><a href="about:license#gears-istumbler">Google Gears/iStumbler License</a></li>
       <li><a href="about:license#vp8">Google VP8 License</a></li>
+      <li><a href="about:license#gsl">GSL License</a></li>
       <li><a href="about:license#gyp">gyp License</a></li>
       <li><a href="about:license#halloc">halloc License</a></li>
       <li><a href="about:license#harfbuzz">HarfBuzz License</a></li>
       <li><a href="about:license#icu">ICU License</a></li>
       <li><a href="about:license#immutable">Immutable.js License</a></li>
       <li><a href="about:license#jpnic">Japan Network Information Center License</a></li>
       <li><a href="about:license#jsmn">JSMN License</a></li>
       <li><a href="about:license#jszip">JSZip License</a></li>
@@ -3412,16 +3413,48 @@ PROCUREMENT OF SUBSTITUTE GOODS OR SERVI
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
     <hr>
 
+    <h1><a id="gsl"></a>GSL License</h1>
+
+    <p>This license applies to <span class="path">mfbt/Span.h</span> and
+    <span class="path">mfbt/tests/gtest/TestSpan.cpp</span>.</p>
+    <!-- https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/LICENSE -->
+<pre>
+Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+
+This code is licensed under the MIT License (MIT).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</pre>
+
+
+    <hr>
+
     <h1><a id="gyp"></a>gyp License</h1>
 
     <p>This license applies to certain files in the directory
     <span class="path">media/webrtc/trunk/tools/gyp</span>.</p>
 <pre>
 Copyright (c) 2009 Google Inc. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
--- a/xpcom/ds/nsTArray.h
+++ b/xpcom/ds/nsTArray.h
@@ -14,16 +14,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/fallible.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/ReverseIterator.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Span.h"
 
 #include <string.h>
 
 #include "nsCycleCollectionNoteChild.h"
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsQuickSort.h"
 #include "nsDebug.h"
@@ -1111,16 +1112,28 @@ public:
   // Methods for reverse iterating.
   reverse_iterator rbegin() { return reverse_iterator(end()); }
   const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
   const_reverse_iterator crbegin() const { return rbegin(); }
   reverse_iterator rend() { return reverse_iterator(begin()); }
   const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
   const_reverse_iterator crend() const { return rend(); }
 
+  // Span integration
+
+  operator mozilla::Span<elem_type>()
+  {
+    return mozilla::Span<elem_type>(Elements(), Length());
+  }
+
+  operator mozilla::Span<const elem_type>() const
+  {
+    return mozilla::Span<const elem_type>(Elements(), Length());
+  }
+
   //
   // Search methods
   //
 
   // This method searches for the first element in this array that is equal
   // to the given element.
   // @param aItem  The item to search for.
   // @param aComp  The Comparator used to determine element equality.
@@ -1335,27 +1348,46 @@ public:
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
                                const nsTArray<Item>& aArray)
   {
     return ReplaceElementsAt<Item, ActualAlloc>(
       aStart, aCount, aArray.Elements(), aArray.Length());
   }
+
+  template<class Item, typename ActualAlloc = Alloc>
+  elem_type* ReplaceElementsAt(index_type aStart,
+                               size_type aCount,
+                               mozilla::Span<const Item> aSpan)
+  {
+    return ReplaceElementsAt<Item, ActualAlloc>(
+      aStart, aCount, aSpan.Elements(), aSpan.Length());
+  }
+
 public:
 
   template<class Item>
   MOZ_MUST_USE
   elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
                                const nsTArray<Item>& aArray,
                                const mozilla::fallible_t&)
   {
     return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aArray);
   }
 
+  template<class Item>
+  MOZ_MUST_USE elem_type* ReplaceElementsAt(index_type aStart,
+                                            size_type aCount,
+                                            mozilla::Span<const Item> aSpan,
+                                            const mozilla::fallible_t&)
+  {
+    return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aSpan);
+  }
+
   // A variation on the ReplaceElementsAt method defined above.
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
                                const Item& aItem)
   {
     return ReplaceElementsAt<Item, ActualAlloc>(aStart, aCount, &aItem, 1);
   }
@@ -1398,27 +1430,44 @@ public:
 protected:
   template<class Item, class Allocator, typename ActualAlloc = Alloc>
   elem_type* InsertElementsAt(index_type aIndex,
                               const nsTArray_Impl<Item, Allocator>& aArray)
   {
     return ReplaceElementsAt<Item, ActualAlloc>(
       aIndex, 0, aArray.Elements(), aArray.Length());
   }
+
+  template<class Item, typename ActualAlloc = Alloc>
+  elem_type* InsertElementsAt(index_type aIndex,
+                              mozilla::Span<const Item> aSpan)
+  {
+    return ReplaceElementsAt<Item, ActualAlloc>(
+      aIndex, 0, aSpan.Elements(), aSpan.Length());
+  }
+
 public:
 
   template<class Item, class Allocator>
   MOZ_MUST_USE
   elem_type* InsertElementsAt(index_type aIndex,
                               const nsTArray_Impl<Item, Allocator>& aArray,
                               const mozilla::fallible_t&)
   {
     return InsertElementsAt<Item, Allocator, FallibleAlloc>(aIndex, aArray);
   }
 
+  template<class Item>
+  MOZ_MUST_USE elem_type* InsertElementsAt(index_type aIndex,
+                                           mozilla::Span<const Item> aSpan,
+                                           const mozilla::fallible_t&)
+  {
+    return InsertElementsAt<Item, FallibleAlloc>(aIndex, aSpan);
+  }
+
   // Insert a new element without copy-constructing. This is useful to avoid
   // temporaries.
   // @return A pointer to the newly inserted element, or null on OOM.
 protected:
   template<typename ActualAlloc = Alloc>
   elem_type* InsertElementAt(index_type aIndex);
 
 public:
@@ -1543,32 +1592,48 @@ public:
   // @param aArray    The elements to append to this array.
   // @param aArrayLen The number of elements to append to this array.
   // @return          A pointer to the new elements in the array, or null if
   //                  the operation failed due to insufficient memory.
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const Item* aArray, size_type aArrayLen);
 
+  template<class Item, typename ActualAlloc = Alloc>
+  elem_type* AppendElements(mozilla::Span<const Item> aSpan)
+  {
+    return AppendElements<Item, FallibleAlloc>(aSpan.Elements(),
+                                               aSpan.Length());
+  }
+
   template<class Item, size_t Length, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const mozilla::Array<Item, Length>& aArray)
   {
     return AppendElements<Item, ActualAlloc>(&aArray[0], Length);
   }
 
 public:
 
   template<class Item>
   /* MOZ_MUST_USE */
   elem_type* AppendElements(const Item* aArray, size_type aArrayLen,
                             const mozilla::fallible_t&)
   {
     return AppendElements<Item, FallibleAlloc>(aArray, aArrayLen);
   }
 
+  template<class Item>
+  /* MOZ_MUST_USE */
+  elem_type* AppendElements(mozilla::Span<const Item> aSpan,
+                            const mozilla::fallible_t&)
+  {
+    return AppendElements<Item, FallibleAlloc>(aSpan.Elements(),
+                                               aSpan.Length());
+  }
+
   // A variation on the AppendElements method defined above.
 protected:
   template<class Item, class Allocator, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const nsTArray_Impl<Item, Allocator>& aArray)
   {
     return AppendElements<Item, ActualAlloc>(aArray.Elements(), aArray.Length());
   }
 public:
@@ -2392,16 +2457,35 @@ class AutoTArray<E, 0> : public nsTArray
 };
 
 template<class E, size_t N>
 struct nsTArray_CopyChooser<AutoTArray<E, N>>
 {
   typedef nsTArray_CopyWithConstructors<AutoTArray<E, N>> Type;
 };
 
+// Span integration
+namespace mozilla {
+
+template<class ElementType, class TArrayAlloc>
+Span<ElementType>
+MakeSpan(nsTArray_Impl<ElementType, TArrayAlloc>& aTArray)
+{
+  return aTArray;
+}
+
+template<class ElementType, class TArrayAlloc>
+Span<const ElementType>
+MakeSpan(const nsTArray_Impl<ElementType, TArrayAlloc>& aTArray)
+{
+  return aTArray;
+}
+
+} // namespace mozilla
+
 // Assert that AutoTArray doesn't have any extra padding inside.
 //
 // It's important that the data stored in this auto array takes up a multiple of
 // 8 bytes; e.g. AutoTArray<uint32_t, 1> wouldn't work.  Since AutoTArray
 // contains a pointer, its size must be a multiple of alignof(void*).  (This is
 // because any type may be placed into an array, and there's no padding between
 // elements of an array.)  The compiler pads the end of the structure to
 // enforce this rule.
--- a/xpcom/string/nsTSubstring.h
+++ b/xpcom/string/nsTSubstring.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 // IWYU pragma: private, include "nsString.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Span.h"
 
 #ifndef MOZILLA_INTERNAL_API
 #error "Using XPCOM strings is limited to code linked into libxul."
 #endif
 
 /**
  * The base for string comparators
  */
@@ -917,16 +919,78 @@ public:
   size_type GetMutableData(wchar_t** aData, size_type aNewLen,
                            const fallible_t& aFallible)
   {
     return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen,
                           aFallible);
   }
 #endif
 
+  /**
+   * Span integration
+   */
+
+  operator mozilla::Span<char_type>()
+  {
+    return mozilla::MakeSpan(BeginWriting(), Length());
+  }
+
+  operator mozilla::Span<const char_type>() const
+  {
+    return mozilla::MakeSpan(BeginReading(), Length());
+  }
+
+  void Append(mozilla::Span<const char_type> aSpan)
+  {
+    auto len = aSpan.Length();
+    MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
+    Append(aSpan.Elements(), len);
+  }
+
+  MOZ_MUST_USE bool Append(mozilla::Span<const char_type> aSpan,
+                           const fallible_t& aFallible)
+  {
+    auto len = aSpan.Length();
+    if (len > mozilla::MaxValue<size_type>::value) {
+      return false;
+    }
+    return Append(aSpan.Elements(), len, aFallible);
+  }
+
+#if !defined(CharT_is_PRUnichar)
+  operator mozilla::Span<uint8_t>()
+  {
+    return mozilla::MakeSpan(reinterpret_cast<uint8_t*>(BeginWriting()),
+                             Length());
+  }
+
+  operator mozilla::Span<const uint8_t>() const
+  {
+    return mozilla::MakeSpan(reinterpret_cast<const uint8_t*>(BeginReading()),
+                             Length());
+  }
+
+  void Append(mozilla::Span<const uint8_t> aSpan)
+  {
+    auto len = aSpan.Length();
+    MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
+    Append(reinterpret_cast<const char*>(aSpan.Elements()), len);
+  }
+
+  MOZ_MUST_USE bool Append(mozilla::Span<const uint8_t> aSpan,
+                           const fallible_t& aFallible)
+  {
+    auto len = aSpan.Length();
+    if (len > mozilla::MaxValue<size_type>::value) {
+      return false;
+    }
+    return Append(
+      reinterpret_cast<const char*>(aSpan.Elements()), len, aFallible);
+  }
+#endif
 
   /**
    * string data is never null, but can be marked void.  if true, the
    * string will be truncated.  @see nsTSubstring::IsVoid
    */
 
   void NS_FASTCALL SetIsVoid(bool);
 
@@ -1267,8 +1331,27 @@ public:
   }
 
   const nsTDependentSubstring_CharT& Get(const size_type index) const
   {
     MOZ_ASSERT(index < mArraySize);
     return mArray[index];
   }
 };
+
+/**
+ * Span integration
+ */
+namespace mozilla {
+
+inline Span<CharT>
+MakeSpan(nsTSubstring_CharT& aString)
+{
+  return aString;
+}
+
+inline Span<const CharT>
+MakeSpan(const nsTSubstring_CharT& aString)
+{
+  return aString;
+}
+
+} // namespace mozilla