Merge commit '3bc509996b3f78980f4462393228d9f57e0b00c9' as 'third_party/GSL'
Change-Id: Idd4850195e2a433c2df378f7bc7f2d6d546ab3d1
diff --git a/third_party/GSL/.clang-format b/third_party/GSL/.clang-format
new file mode 100644
index 0000000..78696f5
--- /dev/null
+++ b/third_party/GSL/.clang-format
@@ -0,0 +1,21 @@
+ColumnLimit: 100
+
+UseTab: Never
+IndentWidth: 4
+AccessModifierOffset: -4
+NamespaceIndentation: Inner
+
+BreakBeforeBraces: Allman
+AlwaysBreakTemplateDeclarations: true
+BreakConstructorInitializersBeforeComma: true
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+AllowShortBlocksOnASingleLine: true
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+
+PointerAlignment: Left
+AlignConsecutiveAssignments: false
+AlignTrailingComments: true
+
+SpaceAfterCStyleCast: true
diff --git a/third_party/GSL/.gitignore b/third_party/GSL/.gitignore
new file mode 100644
index 0000000..326971f
--- /dev/null
+++ b/third_party/GSL/.gitignore
@@ -0,0 +1,16 @@
+CMakeFiles
+build
+tests/CMakeFiles
+tests/Debug
+*.opensdf
+*.sdf
+tests/*tests.dir
+*.vcxproj
+*.vcxproj.filters
+*.sln
+*.tlog
+Testing/Temporary/*.*
+CMakeCache.txt
+*.suo
+.vs/
+.vscode/
diff --git a/third_party/GSL/.travis.yml b/third_party/GSL/.travis.yml
new file mode 100644
index 0000000..d583eb9
--- /dev/null
+++ b/third_party/GSL/.travis.yml
@@ -0,0 +1,273 @@
+# Based on https://github.com/ldionne/hana/blob/master/.travis.yml
+
+language: cpp
+sudo: false
+notifications:
+ email: false
+
+# Use Linux unless specified otherwise
+os: linux
+dist: trusty
+
+cache:
+ directories:
+ - ${TRAVIS_BUILD_DIR}/deps
+
+matrix:
+ include:
+
+ ##########################################################################
+ # Clang on OSX
+ # Travis seems to take longer to start OSX instances,
+ # so leave this first for the overall build to be faster
+ ##########################################################################
+
+ # XCode 8.3
+ - env: COMPILER=clang++ BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ os: osx
+ osx_image: xcode8.3
+ compiler: clang
+
+ - env: COMPILER=clang++ BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ os: osx
+ osx_image: xcode8.3
+ compiler: clang
+
+ # XCode 9.1
+ - env: COMPILER=clang++ BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ os: osx
+ osx_image: xcode9.1
+ compiler: clang
+
+ - env: COMPILER=clang++ BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ os: osx
+ osx_image: xcode9.1
+ compiler: clang
+
+ ##########################################################################
+ # Clang on Linux
+ ##########################################################################
+
+ # Clang 3.6
+ - env: COMPILER=clang++-3.6 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang36
+ apt:
+ packages:
+ - clang-3.6
+ - g++-5
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.6
+
+ - env: COMPILER=clang++-3.6 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang36
+
+ # Clang 3.7
+ - env: COMPILER=clang++-3.7 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang37
+ apt:
+ packages:
+ - clang-3.7
+ - g++-5
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.7
+
+ - env: COMPILER=clang++-3.7 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang37
+
+ # Clang 3.8
+ - env: COMPILER=clang++-3.8 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang38
+ apt:
+ packages:
+ - clang-3.8
+ - g++-5
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.8
+
+ - env: COMPILER=clang++-3.8 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang38
+
+ # Clang 3.9
+ - env: COMPILER=clang++-3.9 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang39
+ apt:
+ packages:
+ - clang-3.9
+ - g++-5
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.9
+
+ - env: COMPILER=clang++-3.9 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang39
+
+ # Clang 4.0
+ - env: COMPILER=clang++-4.0 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang40
+ apt:
+ packages:
+ - clang-4.0
+ - g++-5
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-trusty-4.0
+
+ - env: COMPILER=clang++-4.0 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang40
+
+ # Clang 5.0
+ - env: COMPILER=clang++-5.0 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang50
+ apt:
+ packages:
+ - clang-5.0
+ - g++-7
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-trusty-5.0
+ - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main'
+ key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
+
+ - env: COMPILER=clang++-5.0 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang50
+
+ - env: COMPILER=clang++-5.0 BUILD_TYPE=Debug GSL_CXX_STANDARD=17
+ addons: *clang50
+
+ - env: COMPILER=clang++-5.0 BUILD_TYPE=Release GSL_CXX_STANDARD=17
+ addons: *clang50
+
+ # Clang 6.0
+ - env: COMPILER=clang++-6.0 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &clang60
+ apt:
+ packages:
+ - clang-6.0
+ - g++-7
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-trusty-6.0
+ - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main'
+ key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
+
+ - env: COMPILER=clang++-6.0 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *clang60
+
+ # Clang 6.0 c++17
+ - env: COMPILER=clang++-6.0 BUILD_TYPE=Debug GSL_CXX_STANDARD=17
+ addons: *clang60
+
+ - env: COMPILER=clang++-6.0 BUILD_TYPE=Release GSL_CXX_STANDARD=17
+ addons: *clang60
+
+ ##########################################################################
+ # GCC on Linux
+ ##########################################################################
+
+ # GCC 5
+ - env: COMPILER=g++-5 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &gcc5
+ apt:
+ packages: g++-5
+ sources:
+ - ubuntu-toolchain-r-test
+
+ - env: COMPILER=g++-5 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *gcc5
+
+ # GCC 6
+ - env: COMPILER=g++-6 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &gcc6
+ apt:
+ packages: g++-6
+ sources:
+ - ubuntu-toolchain-r-test
+
+ - env: COMPILER=g++-6 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *gcc6
+
+ # GCC 7
+ - env: COMPILER=g++-7 BUILD_TYPE=Debug GSL_CXX_STANDARD=14
+ addons: &gcc7
+ apt:
+ packages: g++-7
+ sources:
+ - ubuntu-toolchain-r-test
+
+ - env: COMPILER=g++-7 BUILD_TYPE=Release GSL_CXX_STANDARD=14
+ addons: *gcc7
+
+ # GCC 7 c++17
+ - env: COMPILER=g++-7 BUILD_TYPE=Debug GSL_CXX_STANDARD=17
+ addons: *gcc7
+
+ - env: COMPILER=g++-7 BUILD_TYPE=Release GSL_CXX_STANDARD=17
+ addons: *gcc7
+
+install:
+ # Set the ${CXX} variable properly
+ - export CXX=${COMPILER}
+ - ${CXX} --version
+
+ # Dependencies required by the CI are installed in ${TRAVIS_BUILD_DIR}/deps/
+ - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
+ - mkdir -p "${DEPS_DIR}"
+ - cd "${DEPS_DIR}"
+
+ # Travis machines have 2 cores
+ - JOBS=2
+
+ ############################################################################
+ # Install a recent CMake (unless already installed on OS X)
+ ############################################################################
+ - CMAKE_VERSION=3.7.2
+ - |
+ if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
+ CMAKE_URL="https://cmake.org/files/v${CMAKE_VERSION%.[0-9]}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz"
+ mkdir cmake && travis_retry wget --no-check-certificate -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
+ export PATH=${DEPS_DIR}/cmake/bin:${PATH}
+ else
+ brew install cmake || brew upgrade cmake
+ fi
+ - cmake --version
+
+ ############################################################################
+ # [linux]: Install the right version of libc++
+ ############################################################################
+ - |
+ LLVM_INSTALL=${DEPS_DIR}/llvm/install
+ # if in linux and compiler clang and llvm not installed
+ if [[ "${TRAVIS_OS_NAME}" == "linux" && "${CXX%%+*}" == "clang" && -n "$(ls -A ${LLVM_INSTALL})" ]]; then
+ if [[ "${CXX}" == "clang++-3.6" ]]; then LLVM_VERSION="3.6.2";
+ elif [[ "${CXX}" == "clang++-3.7" ]]; then LLVM_VERSION="3.7.1";
+ elif [[ "${CXX}" == "clang++-3.8" ]]; then LLVM_VERSION="3.8.1";
+ elif [[ "${CXX}" == "clang++-3.9" ]]; then LLVM_VERSION="3.9.1";
+ fi
+ LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz"
+ LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz"
+ LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxxabi-${LLVM_VERSION}.src.tar.xz"
+ mkdir -p llvm llvm/build llvm/projects/libcxx llvm/projects/libcxxabi
+ travis_retry wget -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C llvm
+ travis_retry wget -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxx
+ travis_retry wget -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxxabi
+ (cd llvm/build && cmake .. -DCMAKE_INSTALL_PREFIX=${LLVM_INSTALL})
+ (cd llvm/build/projects/libcxx && make install -j2)
+ (cd llvm/build/projects/libcxxabi && make install -j2)
+ export CXXFLAGS="-isystem ${LLVM_INSTALL}/include/c++/v1"
+ export LDFLAGS="-L ${LLVM_INSTALL}/lib -l c++ -l c++abi"
+ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_INSTALL}/lib"
+ fi
+
+before_script:
+ # have CMake to generate build files
+ - cd "${TRAVIS_BUILD_DIR}"
+ - mkdir build && cd build
+ - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DGSL_CXX_STANDARD=$GSL_CXX_STANDARD
+
+script:
+ # build and run tests
+ - cmake --build . -- -j${JOBS}
+ - ctest --output-on-failure -j${JOBS}
diff --git a/third_party/GSL/CMakeLists.txt b/third_party/GSL/CMakeLists.txt
new file mode 100644
index 0000000..b0de85d
--- /dev/null
+++ b/third_party/GSL/CMakeLists.txt
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 3.1.3)
+
+project(GSL CXX)
+
+include(ExternalProject)
+find_package(Git)
+
+# creates a library GSL which is an interface (header files only)
+add_library(GSL INTERFACE)
+
+# determine whether this is a standalone project or included by other projects
+set(GSL_STANDALONE_PROJECT OFF)
+if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
+ set(GSL_STANDALONE_PROJECT ON)
+endif ()
+
+set(GSL_CXX_STANDARD "14" CACHE STRING "Use c++ standard")
+set(GSL_CXX_STD "cxx_std_${GSL_CXX_STANDARD}")
+
+if (MSVC)
+ set(GSL_CXX_STD_OPT "-std:c++${GSL_CXX_STANDARD}")
+else()
+ set(GSL_CXX_STD_OPT "-std=c++${GSL_CXX_STANDARD}")
+endif()
+
+# when minimum version required is 3.8.0 remove if below
+# both branches do exactly the same thing
+if (CMAKE_VERSION VERSION_LESS 3.7.9)
+ include(CheckCXXCompilerFlag)
+ CHECK_CXX_COMPILER_FLAG("${GSL_CXX_STD_OPT}" COMPILER_SUPPORTS_CXX_STANDARD)
+
+ if(COMPILER_SUPPORTS_CXX_STANDARD)
+ target_compile_options(GSL INTERFACE "${GSL_CXX_STD_OPT}")
+ else()
+ message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no c++${GSL_CXX_STANDARD} support. Please use a different C++ compiler.")
+ endif()
+else ()
+ target_compile_features(GSL INTERFACE "${GSL_CXX_STD}")
+ # on *nix systems force the use of -std=c++XX instead of -std=gnu++XX (default)
+ set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+# add definitions to the library and targets that consume it
+target_compile_definitions(GSL INTERFACE
+ $<$<CXX_COMPILER_ID:MSVC>:
+ # remove unnecessary warnings about unchecked iterators
+ _SCL_SECURE_NO_WARNINGS
+ # remove deprecation warnings about std::uncaught_exception() (from catch)
+ _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
+ >
+)
+
+# add include folders to the library and targets that consume it
+target_include_directories(GSL INTERFACE
+ $<BUILD_INTERFACE:
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ >
+)
+
+if ((CMAKE_VERSION GREATER 3.7.9) OR (CMAKE_VERSION EQUAL 3.7.9))
+ if (MSVC_IDE)
+ option(VS_ADD_NATIVE_VISUALIZERS "Configure project to use Visual Studio native visualizers" TRUE)
+ else()
+ set(VS_ADD_NATIVE_VISUALIZERS FALSE CACHE INTERNAL "Native visualizers are Visual Studio extension" FORCE)
+ endif()
+
+ # add natvis file to the library so it will automatically be loaded into Visual Studio
+ if(VS_ADD_NATIVE_VISUALIZERS)
+ target_sources(GSL INTERFACE
+ ${CMAKE_CURRENT_SOURCE_DIR}/GSL.natvis
+ )
+ endif()
+endif()
+
+install(
+ DIRECTORY include/gsl
+ DESTINATION include
+)
+
+option(GSL_TEST "Generate tests." ${GSL_STANDALONE_PROJECT})
+if (GSL_TEST)
+ enable_testing()
+ add_subdirectory(tests)
+endif ()
diff --git a/third_party/GSL/CONTRIBUTING.md b/third_party/GSL/CONTRIBUTING.md
new file mode 100644
index 0000000..10e6c32
--- /dev/null
+++ b/third_party/GSL/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+## Contributing to the Guideline Support Library
+
+The Guideline Support Library (GSL) contains functions and types that are suggested for use by the
+[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). GSL design changes are made only as a result of modifications to the Guidelines.
+
+GSL is accepting contributions that improve or refine any of the types in this library as well as ports to other platforms. Changes should have an issue
+tracking the suggestion that has been approved by the maintainers. Your pull request should include a link to the bug that you are fixing. If you've submitted
+a PR, please post a comment in the associated issue to avoid duplication of effort.
+
+## Legal
+You will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us and the community permission to
+use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright.
+
+Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally.
+
+## Housekeeping
+Your pull request should:
+
+* Include a description of what your change intends to do
+* Be a child commit of a reasonably recent commit in the **master** branch
+ * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR)
+* It is desirable, but not necessary, for the tests to pass at each commit. Please see [README.md](./README.md) for instructions to build the test suite.
+* Have clear commit messages
+ * e.g. "Fix issue", "Add tests for type", etc.
+* Include appropriate tests
+ * Tests should include reasonable permutations of the target fix/change
+ * Include baseline changes with your change
+ * All changed code must have 100% code coverage
+* To avoid line ending issues, set `autocrlf = input` and `whitespace = cr-at-eol` in your git configuration
diff --git a/third_party/GSL/GSL.natvis b/third_party/GSL/GSL.natvis
new file mode 100644
index 0000000..a467a17
--- /dev/null
+++ b/third_party/GSL/GSL.natvis
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This will make GitHub and some editors recognize this code as XML:
+ vim: syntax=xml
+-->
+<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+ <!-- These types are from the gsl_assert header. -->
+ <Type Name="gsl::fail_fast">
+ <!-- na hides the address, otherwise it would appear as 0x.... "Message" -->
+ <DisplayString>{_Data._What,nasb}</DisplayString>
+ </Type>
+
+ <!-- These types are from the gsl_util header. -->
+ <Type Name="gsl::final_action<*>">
+ <DisplayString>{{ invoke = {invoke_}, action = {f_} }}</DisplayString>
+ <Expand>
+ <Item Name="[invoke]">invoke_</Item>
+ <Item Name="[callback]">f_</Item>
+ </Expand>
+ </Type>
+
+ <!-- These types are from the span header. -->
+ <!-- This is for dynamic_extent spans. -->
+ <Type Name="gsl::span<*, -1>">
+ <DisplayString>{{ extent = {storage_.size_} }}</DisplayString>
+ <Expand>
+ <ArrayItems>
+ <Size>storage_.size_</Size>
+ <ValuePointer>storage_.data_</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <!-- This works for constexpr size spans. -->
+ <Type Name="gsl::span<*, *>">
+ <DisplayString>{{ extent = {extent} }}</DisplayString>
+ <Expand>
+ <ArrayItems>
+ <Size>extent</Size>
+ <ValuePointer>storage_.data_</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <!-- This is for dynamic_extent string_spans. -->
+ <Type Name="gsl::basic_string_span<*, -1>">
+ <DisplayString>{span_.storage_.data_,[span_.storage_.size_]na}</DisplayString>
+ <Expand>
+ <Item Name="[size]">span_.storage_.size_</Item>
+ <ArrayItems>
+ <Size>span_.storage_.size_</Size>
+ <ValuePointer>span_.storage_.data_</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <!-- This works for constexpr size string_spans. -->
+ <Type Name="gsl::basic_string_span<*, *>">
+ <DisplayString>{span_.storage_.data_,[span_.extent]na}</DisplayString>
+ <Expand>
+ <Item Name="[size]">span_.extent</Item>
+ <ArrayItems>
+ <Size>span_.extent</Size>
+ <ValuePointer>span_.storage_.data_</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <!-- This is for dynamic_extent zstring_spans. -->
+ <Type Name="gsl::basic_zstring_span<*, -1>">
+ <DisplayString>{span_.storage_.data_,[span_.storage_.size_]na}</DisplayString>
+ <Expand>
+ <Item Name="[size]">span_.storage_.size_</Item>
+ <ArrayItems>
+ <Size>span_.storage_.size_</Size>
+ <ValuePointer>span_.storage_.data_</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <!-- This works for constexpr size string_spans. -->
+ <Type Name="gsl::basic_zstring_span<*, *>">
+ <DisplayString>{span_.storage_.data_,[span_.extent]na}</DisplayString>
+ <Expand>
+ <Item Name="[size]">span_.extent</Item>
+ <ArrayItems>
+ <Size>span_.extent</Size>
+ <ValuePointer>span_.storage_.data_</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <!-- These types are from the gsl header. -->
+ <Type Name="gsl::not_null<*>">
+ <!-- We can always dereference this since it's an invariant. -->
+ <DisplayString>value = {*ptr_}</DisplayString>
+ </Type>
+</AutoVisualizer>
diff --git a/third_party/GSL/LICENSE b/third_party/GSL/LICENSE
new file mode 100644
index 0000000..aa58667
--- /dev/null
+++ b/third_party/GSL/LICENSE
@@ -0,0 +1,21 @@
+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.
diff --git a/third_party/GSL/README.md b/third_party/GSL/README.md
new file mode 100644
index 0000000..9fea9ae
--- /dev/null
+++ b/third_party/GSL/README.md
@@ -0,0 +1,94 @@
+# GSL: Guideline Support Library [](https://travis-ci.org/Microsoft/GSL) [](https://ci.appveyor.com/project/neilmacintosh/GSL)
+
+The Guideline Support Library (GSL) contains functions and types that are suggested for use by the
+[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org).
+This repo contains Microsoft's implementation of GSL.
+
+The library includes types like `span<T>`, `string_span`, `owner<>` and others.
+
+The entire implementation is provided inline in the headers under the [gsl](./include/gsl) directory. The implementation generally assumes a platform that implements C++14 support. There are specific workarounds to support MSVC 2015.
+
+While some types have been broken out into their own headers (e.g. [gsl/span](./include/gsl/span)),
+it is simplest to just include [gsl/gsl](./include/gsl/gsl) and gain access to the entire library.
+
+> NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to
+other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing.
+
+# Project Code of Conduct
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+# Usage of Third Party Libraries
+This project makes use of the [Catch](https://github.com/philsquared/catch) testing library. Please see the [ThirdPartyNotices.txt](./ThirdPartyNotices.txt) file for details regarding the licensing of Catch.
+
+# Quick Start
+## Supported Platforms
+The test suite that exercises GSL has been built and passes successfully on the following platforms:<sup>1)</sup>
+
+* Windows using Visual Studio 2015
+* Windows using Visual Studio 2017
+* Windows using Clang/LLVM 3.6
+* Windows using GCC 5.1
+* Windows using Intel C++ Compiler 18.0
+* GNU/Linux using Clang/LLVM 3.6
+* GNU/Linux using GCC 5.1
+* OS X Yosemite using Xcode with Apple Clang 7.0.0.7000072
+* OS X Yosemite using GCC-5.2.0
+* OS X Sierra 10.12.4 using Apple LLVM version 8.1.0 (Clang-802.0.42)
+* OS X El Capitan (10.11) using Xcode with AppleClang 8.0.0.8000042
+* OS X High Sierra 10.13.2 (17C88) using Apple LLVM version 9.0.0 (clang-900.0.39.2)
+* FreeBSD 10.x with Clang/LLVM 3.6
+
+> If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider
+contributing any changes that were necessary back to this project to benefit the wider community.
+
+<sup>1)</sup> For `gsl::byte` to work correctly with Clang and GCC you might have to use the ` -fno-strict-aliasing` compiler option.
+
+## Building the tests
+To build the tests, you will require the following:
+
+* [CMake](http://cmake.org), version 3.1.3 or later to be installed and in your PATH.
+
+These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`.
+
+1. Create a directory to contain the build outputs for a particular architecture (we name it c:\GSL\build-x86 in this example).
+
+ cd GSL
+ md build-x86
+ cd build-x86
+
+2. Configure CMake to use the compiler of your choice (you can see a list by running `cmake --help`).
+
+ cmake -G "Visual Studio 14 2015" c:\GSL
+
+3. Build the test suite (in this case, in the Debug configuration, Release is another good choice).
+
+ cmake --build . --config Debug
+
+4. Run the test suite.
+
+ ctest -C Debug
+
+All tests should pass - indicating your platform is fully supported and you are ready to use the GSL types!
+
+## Using the libraries
+As the types are entirely implemented inline in headers, there are no linking requirements.
+
+You can copy the [gsl](./include/gsl) directory into your source tree so it is available
+to your compiler, then include the appropriate headers in your program.
+
+Alternatively set your compiler's *include path* flag to point to the GSL development folder (`c:\GSL\include` in the example above) or installation folder (after running the install). Eg.
+
+MSVC++
+
+ /I c:\GSL\include
+
+GCC/clang
+
+ -I$HOME/dev/GSL/include
+
+Include the library using:
+
+ #include <gsl/gsl>
+
+## Debugging visualization support
+For Visual Studio users, the file [GSL.natvis](./GSL.natvis) in the root directory of the repository can be added to your project if you would like more helpful visualization of GSL types in the Visual Studio debugger than would be offered by default.
diff --git a/third_party/GSL/ThirdPartyNotices.txt b/third_party/GSL/ThirdPartyNotices.txt
new file mode 100644
index 0000000..94b9acc
--- /dev/null
+++ b/third_party/GSL/ThirdPartyNotices.txt
@@ -0,0 +1,39 @@
+
+THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
+Do Not Translate or Localize
+
+GSL: Guideline Support Library incorporates third party material from the projects listed below. The original copyright notice and the license under which Microsoft received such third party material are set forth below. Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise.
+
+
+1. Catch (https://github.com/philsquared/Catch)
+
+
+%% Catch NOTICES, INFORMATION, AND LICENSE BEGIN HERE
+=========================================
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+=========================================
+END OF Catch NOTICES, INFORMATION, AND LICENSE
+
+
diff --git a/third_party/GSL/appveyor.yml b/third_party/GSL/appveyor.yml
new file mode 100644
index 0000000..2fc8a16
--- /dev/null
+++ b/third_party/GSL/appveyor.yml
@@ -0,0 +1,58 @@
+shallow_clone: true
+
+platform:
+ - x86
+ - x64
+
+configuration:
+ - Debug
+ - Release
+
+image:
+ - Visual Studio 2015
+ - Visual Studio 2017
+
+environment:
+ matrix:
+ - GSL_CXX_STANDARD: 14
+ - GSL_CXX_STANDARD: 17
+
+matrix:
+ exclude:
+ - image: Visual Studio 2015
+ GSL_CXX_STANDARD: 17
+
+cache:
+ - C:\cmake-3.8.0-win32-x86
+
+install:
+ - ps: |
+ if (![IO.File]::Exists("C:\cmake-3.8.0-win32-x86\bin\cmake.exe")) {
+ Start-FileDownload 'https://cmake.org/files/v3.8/cmake-3.8.0-win32-x86.zip'
+ 7z x -y cmake-3.8.0-win32-x86.zip -oC:\
+ }
+ $env:PATH="C:\cmake-3.8.0-win32-x86\bin;$env:PATH"
+
+before_build:
+ - ps: |
+ mkdir build
+ cd build
+
+ if ("$env:APPVEYOR_JOB_NAME" -match "Image: Visual Studio 2015") {
+ $env:generator="Visual Studio 14 2015"
+ } else {
+ $env:generator="Visual Studio 15 2017"
+ }
+ if ($env:PLATFORM -eq "x64") {
+ $env:generator="$env:generator Win64"
+ }
+ echo generator="$env:generator"
+ cmake .. -G "$env:generator" -DGSL_CXX_STANDARD="$env:GSL_CXX_STANDARD"
+
+build_script:
+ - cmake --build . --config %CONFIGURATION% -- /m /v:minimal
+
+test_script:
+ - ctest -j2
+
+deploy: off
diff --git a/third_party/GSL/include/gsl/gsl b/third_party/GSL/include/gsl/gsl
new file mode 100644
index 0000000..55862eb
--- /dev/null
+++ b/third_party/GSL/include/gsl/gsl
@@ -0,0 +1,29 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_GSL_H
+#define GSL_GSL_H
+
+#include <gsl/gsl_algorithm> // copy
+#include <gsl/gsl_assert> // Ensures/Expects
+#include <gsl/gsl_byte> // byte
+#include <gsl/gsl_util> // finally()/narrow()/narrow_cast()...
+#include <gsl/multi_span> // multi_span, strided_span...
+#include <gsl/pointers> // owner, not_null
+#include <gsl/span> // span
+#include <gsl/string_span> // zstring, string_span, zstring_builder...
+
+#endif // GSL_GSL_H
diff --git a/third_party/GSL/include/gsl/gsl_algorithm b/third_party/GSL/include/gsl/gsl_algorithm
new file mode 100644
index 0000000..710792f
--- /dev/null
+++ b/third_party/GSL/include/gsl/gsl_algorithm
@@ -0,0 +1,63 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_ALGORITHM_H
+#define GSL_ALGORITHM_H
+
+#include <gsl/gsl_assert> // for Expects
+#include <gsl/span> // for dynamic_extent, span
+
+#include <algorithm> // for copy_n
+#include <cstddef> // for ptrdiff_t
+#include <type_traits> // for is_assignable
+
+#ifdef _MSC_VER
+#pragma warning(push)
+
+// turn off some warnings that are noisy about our Expects statements
+#pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4996) // unsafe use of std::copy_n
+
+// 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)
+#endif // _MSC_VER
+
+namespace gsl
+{
+
+template <class SrcElementType, std::ptrdiff_t SrcExtent, class DestElementType,
+ std::ptrdiff_t DestExtent>
+void copy(span<SrcElementType, SrcExtent> src, span<DestElementType, DestExtent> dest)
+{
+ static_assert(std::is_assignable<decltype(*dest.data()), decltype(*src.data())>::value,
+ "Elements of source span can not be assigned to elements of destination span");
+ static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent ||
+ (SrcExtent <= DestExtent),
+ "Source range is longer than target range");
+
+ Expects(dest.size() >= src.size());
+ std::copy_n(src.data(), src.size(), dest.data());
+}
+
+} // namespace gsl
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#endif // GSL_ALGORITHM_H
diff --git a/third_party/GSL/include/gsl/gsl_assert b/third_party/GSL/include/gsl/gsl_assert
new file mode 100644
index 0000000..131fa8b
--- /dev/null
+++ b/third_party/GSL/include/gsl/gsl_assert
@@ -0,0 +1,145 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_CONTRACTS_H
+#define GSL_CONTRACTS_H
+
+#include <exception>
+#include <stdexcept> // for logic_error
+
+//
+// Temporary until MSVC STL supports no-exceptions mode.
+// Currently terminate is a no-op in this mode, so we add termination behavior back
+//
+#if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS
+#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND
+#endif
+
+//
+// There are three configuration options for this GSL implementation's behavior
+// when pre/post conditions on the GSL types are violated:
+//
+// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default)
+// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown
+// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens
+//
+#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || \
+ defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION))
+#define GSL_TERMINATE_ON_CONTRACT_VIOLATION
+#endif
+
+#define GSL_STRINGIFY_DETAIL(x) #x
+#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x)
+
+#if defined(__clang__) || defined(__GNUC__)
+#define GSL_LIKELY(x) __builtin_expect(!!(x), 1)
+#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define GSL_LIKELY(x) (!!(x))
+#define GSL_UNLIKELY(x) (!!(x))
+#endif
+
+//
+// GSL_ASSUME(cond)
+//
+// Tell the optimizer that the predicate cond must hold. It is unspecified
+// whether or not cond is actually evaluated.
+//
+#ifdef _MSC_VER
+#define GSL_ASSUME(cond) __assume(cond)
+#elif defined(__GNUC__)
+#define GSL_ASSUME(cond) ((cond) ? static_cast<void>(0) : __builtin_unreachable())
+#else
+#define GSL_ASSUME(cond) static_cast<void>((cond) ? 0 : 0)
+#endif
+
+//
+// GSL.assert: assertions
+//
+
+namespace gsl
+{
+struct fail_fast : public std::logic_error
+{
+ explicit fail_fast(char const* const message) : std::logic_error(message) {}
+};
+
+namespace details
+{
+#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND)
+
+ typedef void (__cdecl *terminate_handler)();
+
+ inline gsl::details::terminate_handler& get_terminate_handler() noexcept
+ {
+ static terminate_handler handler = &abort;
+ return handler;
+ }
+
+#endif
+
+ [[noreturn]] inline void terminate() noexcept
+ {
+#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND)
+ (*gsl::details::get_terminate_handler())();
+#else
+ std::terminate();
+#endif
+ }
+
+#if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION)
+
+ template <typename Exception>
+ [[noreturn]] void throw_exception(Exception&&)
+ {
+ gsl::details::terminate();
+ }
+
+#else
+
+ template <typename Exception>
+ [[noreturn]] void throw_exception(Exception&& exception)
+ {
+ throw std::forward<Exception>(exception);
+ }
+
+#endif
+
+} // namespace details
+} // namespace gsl
+
+#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
+
+#define GSL_CONTRACT_CHECK(type, cond) \
+ (GSL_LIKELY(cond) ? static_cast<void>(0) \
+ : gsl::details::throw_exception(gsl::fail_fast( \
+ "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__))))
+
+#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION)
+
+#define GSL_CONTRACT_CHECK(type, cond) \
+ (GSL_LIKELY(cond) ? static_cast<void>(0) : gsl::details::terminate())
+
+#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)
+
+#define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond)
+
+#endif
+
+#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond)
+#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond)
+
+#endif // GSL_CONTRACTS_H
diff --git a/third_party/GSL/include/gsl/gsl_byte b/third_party/GSL/include/gsl/gsl_byte
new file mode 100644
index 0000000..e861173
--- /dev/null
+++ b/third_party/GSL/include/gsl/gsl_byte
@@ -0,0 +1,181 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_BYTE_H
+#define GSL_BYTE_H
+
+#include <type_traits>
+
+#ifdef _MSC_VER
+
+#pragma warning(push)
+
+// don't warn about function style casts in byte related operators
+#pragma warning(disable : 26493)
+
+#ifndef GSL_USE_STD_BYTE
+// this tests if we are under MSVC and the standard lib has std::byte and it is enabled
+#if defined(_HAS_STD_BYTE) && _HAS_STD_BYTE
+
+#define GSL_USE_STD_BYTE 1
+
+#else // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE
+
+#define GSL_USE_STD_BYTE 0
+
+#endif // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE
+#endif // GSL_USE_STD_BYTE
+
+#else // _MSC_VER
+
+#ifndef GSL_USE_STD_BYTE
+// this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte
+#if defined(__cplusplus) && (__cplusplus >= 201703L)
+
+#define GSL_USE_STD_BYTE 1
+#include <cstddef>
+
+#else // defined(__cplusplus) && (__cplusplus >= 201703L)
+
+#define GSL_USE_STD_BYTE 0
+
+#endif //defined(__cplusplus) && (__cplusplus >= 201703L)
+#endif // GSL_USE_STD_BYTE
+
+#endif // _MSC_VER
+
+// Use __may_alias__ attribute on gcc and clang
+#if defined __clang__ || (__GNUC__ > 5)
+#define byte_may_alias __attribute__((__may_alias__))
+#else // defined __clang__ || defined __GNUC__
+#define byte_may_alias
+#endif // defined __clang__ || defined __GNUC__
+
+namespace gsl
+{
+#if GSL_USE_STD_BYTE
+
+
+using std::byte;
+using std::to_integer;
+
+#else // GSL_USE_STD_BYTE
+
+// This is a simple definition for now that allows
+// use of byte within span<> to be standards-compliant
+enum class byte_may_alias byte : unsigned char
+{
+};
+
+template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
+constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept
+{
+ return b = byte(static_cast<unsigned char>(b) << shift);
+}
+
+template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
+constexpr byte operator<<(byte b, IntegerType shift) noexcept
+{
+ return byte(static_cast<unsigned char>(b) << shift);
+}
+
+template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
+constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept
+{
+ return b = byte(static_cast<unsigned char>(b) >> shift);
+}
+
+template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
+constexpr byte operator>>(byte b, IntegerType shift) noexcept
+{
+ return byte(static_cast<unsigned char>(b) >> shift);
+}
+
+constexpr byte& operator|=(byte& l, byte r) noexcept
+{
+ return l = byte(static_cast<unsigned char>(l) | static_cast<unsigned char>(r));
+}
+
+constexpr byte operator|(byte l, byte r) noexcept
+{
+ return byte(static_cast<unsigned char>(l) | static_cast<unsigned char>(r));
+}
+
+constexpr byte& operator&=(byte& l, byte r) noexcept
+{
+ return l = byte(static_cast<unsigned char>(l) & static_cast<unsigned char>(r));
+}
+
+constexpr byte operator&(byte l, byte r) noexcept
+{
+ return byte(static_cast<unsigned char>(l) & static_cast<unsigned char>(r));
+}
+
+constexpr byte& operator^=(byte& l, byte r) noexcept
+{
+ return l = byte(static_cast<unsigned char>(l) ^ static_cast<unsigned char>(r));
+}
+
+constexpr byte operator^(byte l, byte r) noexcept
+{
+ return byte(static_cast<unsigned char>(l) ^ static_cast<unsigned char>(r));
+}
+
+constexpr byte operator~(byte b) noexcept { return byte(~static_cast<unsigned char>(b)); }
+
+template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
+constexpr IntegerType to_integer(byte b) noexcept
+{
+ return static_cast<IntegerType>(b);
+}
+
+#endif // GSL_USE_STD_BYTE
+
+template <bool E, typename T>
+constexpr byte to_byte_impl(T t) noexcept
+{
+ static_assert(
+ E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
+ "If you are calling to_byte with an integer contant use: gsl::to_byte<t>() version.");
+ return static_cast<byte>(t);
+}
+template <>
+constexpr byte to_byte_impl<true, unsigned char>(unsigned char t) noexcept
+{
+ return byte(t);
+}
+
+template <typename T>
+constexpr byte to_byte(T t) noexcept
+{
+ return to_byte_impl<std::is_same<T, unsigned char>::value, T>(t);
+}
+
+template <int I>
+constexpr byte to_byte() noexcept
+{
+ static_assert(I >= 0 && I <= 255,
+ "gsl::byte only has 8 bits of storage, values must be in range 0-255");
+ return static_cast<byte>(I);
+}
+
+} // namespace gsl
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#endif // GSL_BYTE_H
diff --git a/third_party/GSL/include/gsl/gsl_util b/third_party/GSL/include/gsl/gsl_util
new file mode 100644
index 0000000..25f8502
--- /dev/null
+++ b/third_party/GSL/include/gsl/gsl_util
@@ -0,0 +1,158 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_UTIL_H
+#define GSL_UTIL_H
+
+#include <gsl/gsl_assert> // for Expects
+
+#include <array>
+#include <cstddef> // for ptrdiff_t, size_t
+#include <exception> // for exception
+#include <initializer_list> // for initializer_list
+#include <type_traits> // for is_signed, integral_constant
+#include <utility> // for forward
+
+#if defined(_MSC_VER)
+
+#pragma warning(push)
+#pragma warning(disable : 4127) // conditional expression is constant
+
+#if _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+#endif // _MSC_VER < 1910
+#endif // _MSC_VER
+
+namespace gsl
+{
+//
+// GSL.util: utilities
+//
+
+// index type for all container indexes/subscripts/sizes
+using index = std::ptrdiff_t;
+
+// final_action allows you to ensure something gets run at the end of a scope
+template <class F>
+class final_action
+{
+public:
+ explicit final_action(F f) noexcept : f_(std::move(f)) {}
+
+ final_action(final_action&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_)
+ {
+ other.invoke_ = false;
+ }
+
+ final_action(const final_action&) = delete;
+ final_action& operator=(const final_action&) = delete;
+ final_action& operator=(final_action&&) = delete;
+
+ ~final_action() noexcept
+ {
+ if (invoke_) f_();
+ }
+
+private:
+ F f_;
+ bool invoke_ {true};
+};
+
+// finally() - convenience function to generate a final_action
+template <class F>
+
+final_action<F> finally(const F& f) noexcept
+{
+ return final_action<F>(f);
+}
+
+template <class F>
+final_action<F> finally(F&& f) noexcept
+{
+ return final_action<F>(std::forward<F>(f));
+}
+
+// narrow_cast(): a searchable way to do narrowing casts of values
+template <class T, class U>
+constexpr T narrow_cast(U&& u) noexcept
+{
+ return static_cast<T>(std::forward<U>(u));
+}
+
+struct narrowing_error : public std::exception
+{
+};
+
+namespace details
+{
+ template <class T, class U>
+ struct is_same_signedness
+ : public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value>
+ {
+ };
+}
+
+// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
+template <class T, class U>
+T narrow(U u)
+{
+ T t = narrow_cast<T>(u);
+ if (static_cast<U>(t) != u) gsl::details::throw_exception(narrowing_error());
+ if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))
+ gsl::details::throw_exception(narrowing_error());
+ return t;
+}
+
+//
+// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector
+//
+template <class T, std::size_t N>
+constexpr T& at(T (&arr)[N], const index i)
+{
+ Expects(i >= 0 && i < narrow_cast<index>(N));
+ return arr[static_cast<std::size_t>(i)];
+}
+
+template <class Cont>
+constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()])
+{
+ Expects(i >= 0 && i < narrow_cast<index>(cont.size()));
+ using size_type = decltype(cont.size());
+ return cont[static_cast<size_type>(i)];
+}
+
+template <class T>
+constexpr T at(const std::initializer_list<T> cont, const index i)
+{
+ Expects(i >= 0 && i < narrow_cast<index>(cont.size()));
+ return *(cont.begin() + i);
+}
+
+} // namespace gsl
+
+#if defined(_MSC_VER)
+#if _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // _MSC_VER < 1910
+
+#pragma warning(pop)
+
+#endif // _MSC_VER
+
+#endif // GSL_UTIL_H
diff --git a/third_party/GSL/include/gsl/multi_span b/third_party/GSL/include/gsl/multi_span
new file mode 100644
index 0000000..9c0c27b
--- /dev/null
+++ b/third_party/GSL/include/gsl/multi_span
@@ -0,0 +1,2242 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_MULTI_SPAN_H
+#define GSL_MULTI_SPAN_H
+
+#include <gsl/gsl_assert> // for Expects
+#include <gsl/gsl_byte> // for byte
+#include <gsl/gsl_util> // for narrow_cast
+
+#include <algorithm> // for transform, lexicographical_compare
+#include <array> // for array
+#include <cassert>
+#include <cstddef> // for ptrdiff_t, size_t, nullptr_t
+#include <cstdint> // for PTRDIFF_MAX
+#include <functional> // for divides, multiplies, minus, negate, plus
+#include <initializer_list> // for initializer_list
+#include <iterator> // for iterator, random_access_iterator_tag
+#include <limits> // for numeric_limits
+#include <new>
+#include <numeric>
+#include <stdexcept>
+#include <string> // for basic_string
+#include <type_traits> // for enable_if_t, remove_cv_t, is_same, is_co...
+#include <utility>
+
+#ifdef _MSC_VER
+
+// turn off some warnings that are noisy about our Expects statements
+#pragma warning(push)
+#pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4702) // unreachable code
+
+#if _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+
+#endif // _MSC_VER < 1910
+#endif // _MSC_VER
+
+// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t)
+// While there is a conversion from signed to unsigned, it happens at
+// compiletime, so the compiler wouldn't have to warn indiscriminently, but
+// could check if the source value actually doesn't fit into the target type
+// and only warn in those cases.
+#if __GNUC__ > 6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+#ifdef GSL_THROW_ON_CONTRACT_VIOLATION
+#define GSL_NOEXCEPT /*noexcept*/
+#else
+#define GSL_NOEXCEPT noexcept
+#endif // GSL_THROW_ON_CONTRACT_VIOLATION
+
+namespace gsl
+{
+
+/*
+** begin definitions of index and bounds
+*/
+namespace details
+{
+ template <typename SizeType>
+ struct SizeTypeTraits
+ {
+ static const SizeType max_value = std::numeric_limits<SizeType>::max();
+ };
+
+ template <typename... Ts>
+ class are_integral : public std::integral_constant<bool, true>
+ {
+ };
+
+ template <typename T, typename... Ts>
+ class are_integral<T, Ts...>
+ : public std::integral_constant<bool,
+ std::is_integral<T>::value && are_integral<Ts...>::value>
+ {
+ };
+}
+
+template <std::size_t Rank>
+class multi_span_index final
+{
+ static_assert(Rank > 0, "Rank must be greater than 0!");
+
+ template <std::size_t OtherRank>
+ friend class multi_span_index;
+
+public:
+ static const std::size_t rank = Rank;
+ using value_type = std::ptrdiff_t;
+ using size_type = value_type;
+ using reference = std::add_lvalue_reference_t<value_type>;
+ using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
+
+ constexpr multi_span_index() GSL_NOEXCEPT {}
+
+ constexpr multi_span_index(const value_type (&values)[Rank]) GSL_NOEXCEPT
+ {
+ std::copy(values, values + Rank, elems);
+ }
+
+ template <typename... Ts, typename = std::enable_if_t<(sizeof...(Ts) == Rank) &&
+ details::are_integral<Ts...>::value>>
+ constexpr multi_span_index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast<value_type>(ds)...}
+ {
+ }
+
+ constexpr multi_span_index(const multi_span_index& other) GSL_NOEXCEPT = default;
+
+ constexpr multi_span_index& operator=(const multi_span_index& rhs) GSL_NOEXCEPT = default;
+
+ // Preconditions: component_idx < rank
+ constexpr reference operator[](std::size_t component_idx)
+ {
+ Expects(component_idx < Rank); // Component index must be less than rank
+ return elems[component_idx];
+ }
+
+ // Preconditions: component_idx < rank
+ constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT
+ {
+ Expects(component_idx < Rank); // Component index must be less than rank
+ return elems[component_idx];
+ }
+
+ constexpr bool operator==(const multi_span_index& rhs) const GSL_NOEXCEPT
+ {
+ return std::equal(elems, elems + rank, rhs.elems);
+ }
+
+ constexpr bool operator!=(const multi_span_index& rhs) const GSL_NOEXCEPT { return !(*this == rhs); }
+
+ constexpr multi_span_index operator+() const GSL_NOEXCEPT { return *this; }
+
+ constexpr multi_span_index operator-() const GSL_NOEXCEPT
+ {
+ multi_span_index ret = *this;
+ std::transform(ret, ret + rank, ret, std::negate<value_type>{});
+ return ret;
+ }
+
+ constexpr multi_span_index operator+(const multi_span_index& rhs) const GSL_NOEXCEPT
+ {
+ multi_span_index ret = *this;
+ ret += rhs;
+ return ret;
+ }
+
+ constexpr multi_span_index operator-(const multi_span_index& rhs) const GSL_NOEXCEPT
+ {
+ multi_span_index ret = *this;
+ ret -= rhs;
+ return ret;
+ }
+
+ constexpr multi_span_index& operator+=(const multi_span_index& rhs) GSL_NOEXCEPT
+ {
+ std::transform(elems, elems + rank, rhs.elems, elems, std::plus<value_type>{});
+ return *this;
+ }
+
+ constexpr multi_span_index& operator-=(const multi_span_index& rhs) GSL_NOEXCEPT
+ {
+ std::transform(elems, elems + rank, rhs.elems, elems, std::minus<value_type>{});
+ return *this;
+ }
+
+ constexpr multi_span_index operator*(value_type v) const GSL_NOEXCEPT
+ {
+ multi_span_index ret = *this;
+ ret *= v;
+ return ret;
+ }
+
+ constexpr multi_span_index operator/(value_type v) const GSL_NOEXCEPT
+ {
+ multi_span_index ret = *this;
+ ret /= v;
+ return ret;
+ }
+
+ friend constexpr multi_span_index operator*(value_type v, const multi_span_index& rhs) GSL_NOEXCEPT
+ {
+ return rhs * v;
+ }
+
+ constexpr multi_span_index& operator*=(value_type v) GSL_NOEXCEPT
+ {
+ std::transform(elems, elems + rank, elems,
+ [v](value_type x) { return std::multiplies<value_type>{}(x, v); });
+ return *this;
+ }
+
+ constexpr multi_span_index& operator/=(value_type v) GSL_NOEXCEPT
+ {
+ std::transform(elems, elems + rank, elems,
+ [v](value_type x) { return std::divides<value_type>{}(x, v); });
+ return *this;
+ }
+
+private:
+ value_type elems[Rank] = {};
+};
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+
+struct static_bounds_dynamic_range_t
+{
+ template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+ constexpr operator T() const GSL_NOEXCEPT
+ {
+ return narrow_cast<T>(-1);
+ }
+};
+
+constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT
+{
+ return true;
+}
+
+constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT
+{
+ return false;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT
+{
+ return narrow_cast<T>(-1) == other;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT
+{
+ return right == left;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT
+{
+ return narrow_cast<T>(-1) != other;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT
+{
+ return right != left;
+}
+
+constexpr static_bounds_dynamic_range_t dynamic_range{};
+#else
+const std::ptrdiff_t dynamic_range = -1;
+#endif
+
+struct generalized_mapping_tag
+{
+};
+struct contiguous_mapping_tag : generalized_mapping_tag
+{
+};
+
+namespace details
+{
+
+ template <std::ptrdiff_t Left, std::ptrdiff_t Right>
+ struct LessThan
+ {
+ static const bool value = Left < Right;
+ };
+
+ template <std::ptrdiff_t... Ranges>
+ struct BoundsRanges
+ {
+ using size_type = std::ptrdiff_t;
+ static const size_type Depth = 0;
+ static const size_type DynamicNum = 0;
+ static const size_type CurrentRange = 1;
+ static const size_type TotalSize = 1;
+
+ // TODO : following signature is for work around VS bug
+ template <typename OtherRange>
+ BoundsRanges(const OtherRange&, bool /* firstLevel */)
+ {
+ }
+
+ BoundsRanges(const std::ptrdiff_t* const) {}
+ BoundsRanges() = default;
+
+ template <typename T, std::size_t Dim>
+ void serialize(T&) const
+ {
+ }
+
+ template <typename T, std::size_t Dim>
+ size_type linearize(const T&) const
+ {
+ return 0;
+ }
+
+ template <typename T, std::size_t Dim>
+ size_type contains(const T&) const
+ {
+ return -1;
+ }
+
+ size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; }
+
+ size_type totalSize() const GSL_NOEXCEPT { return TotalSize; }
+
+ bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; }
+ };
+
+ template <std::ptrdiff_t... RestRanges>
+ struct BoundsRanges<dynamic_range, RestRanges...> : BoundsRanges<RestRanges...>
+ {
+ using Base = BoundsRanges<RestRanges...>;
+ using size_type = std::ptrdiff_t;
+ static const std::size_t Depth = Base::Depth + 1;
+ static const std::size_t DynamicNum = Base::DynamicNum + 1;
+ static const size_type CurrentRange = dynamic_range;
+ static const size_type TotalSize = dynamic_range;
+
+ private:
+ size_type m_bound;
+
+ public:
+ BoundsRanges(const std::ptrdiff_t* const arr)
+ : Base(arr + 1), m_bound(*arr * this->Base::totalSize())
+ {
+ Expects(0 <= *arr);
+ }
+
+ BoundsRanges() : m_bound(0) {}
+
+ template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
+ BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
+ bool /* firstLevel */ = true)
+ : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
+ , m_bound(other.totalSize())
+ {
+ }
+
+ template <typename T, std::size_t Dim = 0>
+ void serialize(T& arr) const
+ {
+ arr[Dim] = elementNum();
+ this->Base::template serialize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, std::size_t Dim = 0>
+ size_type linearize(const T& arr) const
+ {
+ const size_type index = this->Base::totalSize() * arr[Dim];
+ Expects(index < m_bound);
+ return index + this->Base::template linearize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, std::size_t Dim = 0>
+ size_type contains(const T& arr) const
+ {
+ const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
+ if (last == -1) return -1;
+ const ptrdiff_t cur = this->Base::totalSize() * arr[Dim];
+ return cur < m_bound ? cur + last : -1;
+ }
+
+ size_type totalSize() const GSL_NOEXCEPT { return m_bound; }
+
+ size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); }
+
+ size_type elementNum(std::size_t dim) const GSL_NOEXCEPT
+ {
+ if (dim > 0)
+ return this->Base::elementNum(dim - 1);
+ else
+ return elementNum();
+ }
+
+ bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT
+ {
+ return m_bound == rhs.m_bound &&
+ static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
+ }
+ };
+
+ template <std::ptrdiff_t CurRange, std::ptrdiff_t... RestRanges>
+ struct BoundsRanges<CurRange, RestRanges...> : BoundsRanges<RestRanges...>
+ {
+ using Base = BoundsRanges<RestRanges...>;
+ using size_type = std::ptrdiff_t;
+ static const std::size_t Depth = Base::Depth + 1;
+ static const std::size_t DynamicNum = Base::DynamicNum;
+ static const size_type CurrentRange = CurRange;
+ static const size_type TotalSize =
+ Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize;
+
+ BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {}
+ BoundsRanges() = default;
+
+ template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
+ BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
+ bool firstLevel = true)
+ : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
+ {
+ (void) firstLevel;
+ }
+
+ template <typename T, std::size_t Dim = 0>
+ void serialize(T& arr) const
+ {
+ arr[Dim] = elementNum();
+ this->Base::template serialize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, std::size_t Dim = 0>
+ size_type linearize(const T& arr) const
+ {
+ Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range
+ return this->Base::totalSize() * arr[Dim] +
+ this->Base::template linearize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, std::size_t Dim = 0>
+ size_type contains(const T& arr) const
+ {
+ if (arr[Dim] >= CurrentRange) return -1;
+ const size_type last = this->Base::template contains<T, Dim + 1>(arr);
+ if (last == -1) return -1;
+ return this->Base::totalSize() * arr[Dim] + last;
+ }
+
+ size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); }
+
+ size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; }
+
+ size_type elementNum(std::size_t dim) const GSL_NOEXCEPT
+ {
+ if (dim > 0)
+ return this->Base::elementNum(dim - 1);
+ else
+ return elementNum();
+ }
+
+ bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT
+ {
+ return static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
+ }
+ };
+
+ template <typename SourceType, typename TargetType>
+ struct BoundsRangeConvertible
+ : public std::integral_constant<bool, (SourceType::TotalSize >= TargetType::TotalSize ||
+ TargetType::TotalSize == dynamic_range ||
+ SourceType::TotalSize == dynamic_range ||
+ TargetType::TotalSize == 0)>
+ {
+ };
+
+ template <typename TypeChain>
+ struct TypeListIndexer
+ {
+ const TypeChain& obj_;
+ TypeListIndexer(const TypeChain& obj) : obj_(obj) {}
+
+ template <std::size_t N>
+ const TypeChain& getObj(std::true_type)
+ {
+ return obj_;
+ }
+
+ template <std::size_t N, typename MyChain = TypeChain,
+ typename MyBase = typename MyChain::Base>
+ auto getObj(std::false_type)
+ -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>())
+ {
+ return TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>();
+ }
+
+ template <std::size_t N>
+ auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, N == 0>()))
+ {
+ return getObj<N - 1>(std::integral_constant<bool, N == 0>());
+ }
+ };
+
+ template <typename TypeChain>
+ TypeListIndexer<TypeChain> createTypeListIndexer(const TypeChain& obj)
+ {
+ return TypeListIndexer<TypeChain>(obj);
+ }
+
+ template <std::size_t Rank, bool Enabled = (Rank > 1),
+ typename Ret = std::enable_if_t<Enabled, multi_span_index<Rank - 1>>>
+ constexpr Ret shift_left(const multi_span_index<Rank>& other) GSL_NOEXCEPT
+ {
+ Ret ret{};
+ for (std::size_t i = 0; i < Rank - 1; ++i) {
+ ret[i] = other[i + 1];
+ }
+ return ret;
+ }
+}
+
+template <typename IndexType>
+class bounds_iterator;
+
+template <std::ptrdiff_t... Ranges>
+class static_bounds
+{
+public:
+ static_bounds(const details::BoundsRanges<Ranges...>&) {}
+};
+
+template <std::ptrdiff_t FirstRange, std::ptrdiff_t... RestRanges>
+class static_bounds<FirstRange, RestRanges...>
+{
+ using MyRanges = details::BoundsRanges<FirstRange, RestRanges...>;
+
+ MyRanges m_ranges;
+ constexpr static_bounds(const MyRanges& range) : m_ranges(range) {}
+
+ template <std::ptrdiff_t... OtherRanges>
+ friend class static_bounds;
+
+public:
+ static const std::size_t rank = MyRanges::Depth;
+ static const std::size_t dynamic_rank = MyRanges::DynamicNum;
+ static const std::ptrdiff_t static_size = MyRanges::TotalSize;
+
+ using size_type = std::ptrdiff_t;
+ using index_type = multi_span_index<rank>;
+ using const_index_type = std::add_const_t<index_type>;
+ using iterator = bounds_iterator<const_index_type>;
+ using const_iterator = bounds_iterator<const_index_type>;
+ using difference_type = std::ptrdiff_t;
+ using sliced_type = static_bounds<RestRanges...>;
+ using mapping_type = contiguous_mapping_tag;
+
+ constexpr static_bounds(const static_bounds&) = default;
+
+ template <typename SourceType, typename TargetType, std::size_t Rank>
+ struct BoundsRangeConvertible2;
+
+ template <std::size_t Rank, typename SourceType, typename TargetType,
+ typename Ret = BoundsRangeConvertible2<typename SourceType::Base,
+ typename TargetType::Base, Rank>>
+ static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret;
+
+ template <std::size_t Rank, typename SourceType, typename TargetType>
+ static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type;
+
+ template <typename SourceType, typename TargetType, std::size_t Rank>
+ struct BoundsRangeConvertible2
+ : decltype(helpBoundsRangeConvertible<Rank - 1>(
+ SourceType(), TargetType(),
+ std::integral_constant<bool,
+ SourceType::Depth == TargetType::Depth &&
+ (SourceType::CurrentRange == TargetType::CurrentRange ||
+ TargetType::CurrentRange == dynamic_range ||
+ SourceType::CurrentRange == dynamic_range)>()))
+ {
+ };
+
+ template <typename SourceType, typename TargetType>
+ struct BoundsRangeConvertible2<SourceType, TargetType, 0> : std::true_type
+ {
+ };
+
+ template <typename SourceType, typename TargetType, std::ptrdiff_t Rank = TargetType::Depth>
+ struct BoundsRangeConvertible
+ : decltype(helpBoundsRangeConvertible<Rank - 1>(
+ SourceType(), TargetType(),
+ std::integral_constant<bool,
+ SourceType::Depth == TargetType::Depth &&
+ (!details::LessThan<SourceType::CurrentRange,
+ TargetType::CurrentRange>::value ||
+ TargetType::CurrentRange == dynamic_range ||
+ SourceType::CurrentRange == dynamic_range)>()))
+ {
+ };
+
+ template <typename SourceType, typename TargetType>
+ struct BoundsRangeConvertible<SourceType, TargetType, 0> : std::true_type
+ {
+ };
+
+ template <std::ptrdiff_t... Ranges,
+ typename = std::enable_if_t<details::BoundsRangeConvertible<
+ details::BoundsRanges<Ranges...>,
+ details::BoundsRanges<FirstRange, RestRanges...>>::value>>
+ constexpr static_bounds(const static_bounds<Ranges...>& other) : m_ranges(other.m_ranges)
+ {
+ Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges<Ranges...>::DynamicNum == 0) ||
+ MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize());
+ }
+
+ constexpr static_bounds(std::initializer_list<size_type> il)
+ : m_ranges(il.begin())
+ {
+ // Size of the initializer list must match the rank of the array
+ Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) ||
+ MyRanges::DynamicNum == il.size());
+ // Size of the range must be less than the max element of the size type
+ Expects(m_ranges.totalSize() <= PTRDIFF_MAX);
+ }
+
+ constexpr static_bounds() = default;
+
+ constexpr sliced_type slice() const GSL_NOEXCEPT
+ {
+ return sliced_type{static_cast<const details::BoundsRanges<RestRanges...>&>(m_ranges)};
+ }
+
+ constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; }
+
+ constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); }
+
+ constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); }
+
+ constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); }
+
+ constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT
+ {
+ return m_ranges.contains(idx) != -1;
+ }
+
+ constexpr size_type operator[](std::size_t idx) const GSL_NOEXCEPT
+ {
+ return m_ranges.elementNum(idx);
+ }
+
+ template <std::size_t Dim = 0>
+ constexpr size_type extent() const GSL_NOEXCEPT
+ {
+ static_assert(Dim < rank,
+ "dimension should be less than rank (dimension count starts from 0)");
+ return details::createTypeListIndexer(m_ranges).template get<Dim>().elementNum();
+ }
+
+ template <typename IntType>
+ constexpr size_type extent(IntType dim) const GSL_NOEXCEPT
+ {
+ static_assert(std::is_integral<IntType>::value,
+ "Dimension parameter must be supplied as an integral type.");
+ auto real_dim = narrow_cast<std::size_t>(dim);
+ Expects(real_dim < rank);
+
+ return m_ranges.elementNum(real_dim);
+ }
+
+ constexpr index_type index_bounds() const GSL_NOEXCEPT
+ {
+ size_type extents[rank] = {};
+ m_ranges.serialize(extents);
+ return {extents};
+ }
+
+ template <std::ptrdiff_t... Ranges>
+ constexpr bool operator==(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT
+ {
+ return this->size() == rhs.size();
+ }
+
+ template <std::ptrdiff_t... Ranges>
+ constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT
+ {
+ return !(*this == rhs);
+ }
+
+ constexpr const_iterator begin() const GSL_NOEXCEPT
+ {
+ return const_iterator(*this, index_type{});
+ }
+
+ constexpr const_iterator end() const GSL_NOEXCEPT
+ {
+ return const_iterator(*this, this->index_bounds());
+ }
+};
+
+template <std::size_t Rank>
+class strided_bounds
+{
+ template <std::size_t OtherRank>
+ friend class strided_bounds;
+
+public:
+ static const std::size_t rank = Rank;
+ using value_type = std::ptrdiff_t;
+ using reference = std::add_lvalue_reference_t<value_type>;
+ using const_reference = std::add_const_t<reference>;
+ using size_type = value_type;
+ using difference_type = value_type;
+ using index_type = multi_span_index<rank>;
+ using const_index_type = std::add_const_t<index_type>;
+ using iterator = bounds_iterator<const_index_type>;
+ using const_iterator = bounds_iterator<const_index_type>;
+ static const value_type dynamic_rank = rank;
+ static const value_type static_size = dynamic_range;
+ using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
+ using mapping_type = generalized_mapping_tag;
+
+ constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default;
+
+ constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default;
+
+ constexpr strided_bounds(const value_type (&values)[rank], index_type strides)
+ : m_extents(values), m_strides(std::move(strides))
+ {
+ }
+
+ constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT
+ : m_extents(extents),
+ m_strides(strides)
+ {
+ }
+
+ constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; }
+
+ constexpr size_type total_size() const GSL_NOEXCEPT
+ {
+ size_type ret = 0;
+ for (std::size_t i = 0; i < rank; ++i) {
+ ret += (m_extents[i] - 1) * m_strides[i];
+ }
+ return ret + 1;
+ }
+
+ constexpr size_type size() const GSL_NOEXCEPT
+ {
+ size_type ret = 1;
+ for (std::size_t i = 0; i < rank; ++i) {
+ ret *= m_extents[i];
+ }
+ return ret;
+ }
+
+ constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT
+ {
+ for (std::size_t i = 0; i < rank; ++i) {
+ if (idx[i] < 0 || idx[i] >= m_extents[i]) return false;
+ }
+ return true;
+ }
+
+ constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT
+ {
+ size_type ret = 0;
+ for (std::size_t i = 0; i < rank; i++) {
+ Expects(idx[i] < m_extents[i]); // index is out of bounds of the array
+ ret += idx[i] * m_strides[i];
+ }
+ return ret;
+ }
+
+ constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; }
+
+ template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
+ constexpr sliced_type slice() const
+ {
+ return {details::shift_left(m_extents), details::shift_left(m_strides)};
+ }
+
+ template <std::size_t Dim = 0>
+ constexpr size_type extent() const GSL_NOEXCEPT
+ {
+ static_assert(Dim < Rank,
+ "dimension should be less than rank (dimension count starts from 0)");
+ return m_extents[Dim];
+ }
+
+ constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; }
+ constexpr const_iterator begin() const GSL_NOEXCEPT
+ {
+ return const_iterator{*this, index_type{}};
+ }
+
+ constexpr const_iterator end() const GSL_NOEXCEPT
+ {
+ return const_iterator{*this, index_bounds()};
+ }
+
+private:
+ index_type m_extents;
+ index_type m_strides;
+};
+
+template <typename T>
+struct is_bounds : std::integral_constant<bool, false>
+{
+};
+template <std::ptrdiff_t... Ranges>
+struct is_bounds<static_bounds<Ranges...>> : std::integral_constant<bool, true>
+{
+};
+template <std::size_t Rank>
+struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true>
+{
+};
+
+template <typename IndexType>
+class bounds_iterator
+{
+public:
+ static const std::size_t rank = IndexType::rank;
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = IndexType;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using index_type = value_type;
+ using index_size_type = typename IndexType::value_type;
+ template <typename Bounds>
+ explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT
+ : boundary_(bnd.index_bounds()),
+ curr_(std::move(curr))
+ {
+ static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
+ }
+
+ constexpr reference operator*() const GSL_NOEXCEPT { return curr_; }
+
+ constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; }
+
+ constexpr bounds_iterator& operator++() GSL_NOEXCEPT
+ {
+ for (std::size_t i = rank; i-- > 0;) {
+ if (curr_[i] < boundary_[i] - 1) {
+ curr_[i]++;
+ return *this;
+ }
+ curr_[i] = 0;
+ }
+ // If we're here we've wrapped over - set to past-the-end.
+ curr_ = boundary_;
+ return *this;
+ }
+
+ constexpr bounds_iterator operator++(int) GSL_NOEXCEPT
+ {
+ auto ret = *this;
+ ++(*this);
+ return ret;
+ }
+
+ constexpr bounds_iterator& operator--() GSL_NOEXCEPT
+ {
+ if (!less(curr_, boundary_)) {
+ // if at the past-the-end, set to last element
+ for (std::size_t i = 0; i < rank; ++i) {
+ curr_[i] = boundary_[i] - 1;
+ }
+ return *this;
+ }
+ for (std::size_t i = rank; i-- > 0;) {
+ if (curr_[i] >= 1) {
+ curr_[i]--;
+ return *this;
+ }
+ curr_[i] = boundary_[i] - 1;
+ }
+ // If we're here the preconditions were violated
+ // "pre: there exists s such that r == ++s"
+ Expects(false);
+ return *this;
+ }
+
+ constexpr bounds_iterator operator--(int) GSL_NOEXCEPT
+ {
+ auto ret = *this;
+ --(*this);
+ return ret;
+ }
+
+ constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT
+ {
+ bounds_iterator ret{*this};
+ return ret += n;
+ }
+
+ constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT
+ {
+ auto linear_idx = linearize(curr_) + n;
+ std::remove_const_t<value_type> stride = 0;
+ stride[rank - 1] = 1;
+ for (std::size_t i = rank - 1; i-- > 0;) {
+ stride[i] = stride[i + 1] * boundary_[i + 1];
+ }
+ for (std::size_t i = 0; i < rank; ++i) {
+ curr_[i] = linear_idx / stride[i];
+ linear_idx = linear_idx % stride[i];
+ }
+ // index is out of bounds of the array
+ Expects(!less(curr_, index_type{}) && !less(boundary_, curr_));
+ return *this;
+ }
+
+ constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT
+ {
+ bounds_iterator ret{*this};
+ return ret -= n;
+ }
+
+ constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
+
+ constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return linearize(curr_) - linearize(rhs.curr_);
+ }
+
+ constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); }
+
+ constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return curr_ == rhs.curr_;
+ }
+
+ constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return !(*this == rhs);
+ }
+
+ constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return less(curr_, rhs.curr_);
+ }
+
+ constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return !(rhs < *this);
+ }
+
+ constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
+
+ constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return !(rhs > *this);
+ }
+
+ void swap(bounds_iterator& rhs) GSL_NOEXCEPT
+ {
+ std::swap(boundary_, rhs.boundary_);
+ std::swap(curr_, rhs.curr_);
+ }
+
+private:
+ constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT
+ {
+ for (std::size_t i = 0; i < rank; ++i) {
+ if (one[i] < other[i]) return true;
+ }
+ return false;
+ }
+
+ constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT
+ {
+ // TODO: Smarter impl.
+ // Check if past-the-end
+ index_size_type multiplier = 1;
+ index_size_type res = 0;
+ if (!less(idx, boundary_)) {
+ res = 1;
+ for (std::size_t i = rank; i-- > 0;) {
+ res += (idx[i] - 1) * multiplier;
+ multiplier *= boundary_[i];
+ }
+ }
+ else
+ {
+ for (std::size_t i = rank; i-- > 0;) {
+ res += idx[i] * multiplier;
+ multiplier *= boundary_[i];
+ }
+ }
+ return res;
+ }
+
+ value_type boundary_;
+ std::remove_const_t<value_type> curr_;
+};
+
+template <typename IndexType>
+bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n,
+ const bounds_iterator<IndexType>& rhs) GSL_NOEXCEPT
+{
+ return rhs + n;
+}
+
+namespace details
+{
+ template <typename Bounds>
+ constexpr std::enable_if_t<
+ std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value,
+ typename Bounds::index_type>
+ make_stride(const Bounds& bnd) GSL_NOEXCEPT
+ {
+ return bnd.strides();
+ }
+
+ // Make a stride vector from bounds, assuming contiguous memory.
+ template <typename Bounds>
+ constexpr std::enable_if_t<
+ std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::value,
+ typename Bounds::index_type>
+ make_stride(const Bounds& bnd) GSL_NOEXCEPT
+ {
+ auto extents = bnd.index_bounds();
+ typename Bounds::size_type stride[Bounds::rank] = {};
+
+ stride[Bounds::rank - 1] = 1;
+ for (std::size_t i = 1; i < Bounds::rank; ++i) {
+ stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i];
+ }
+ return {stride};
+ }
+
+ template <typename BoundsSrc, typename BoundsDest>
+ void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest)
+ {
+ static_assert(is_bounds<BoundsSrc>::value && is_bounds<BoundsDest>::value,
+ "The src type and dest type must be bounds");
+ static_assert(std::is_same<typename BoundsSrc::mapping_type, contiguous_mapping_tag>::value,
+ "The source type must be a contiguous bounds");
+ static_assert(BoundsDest::static_size == dynamic_range ||
+ BoundsSrc::static_size == dynamic_range ||
+ BoundsDest::static_size == BoundsSrc::static_size,
+ "The source bounds must have same size as dest bounds");
+ Expects(src.size() == dest.size());
+ }
+
+} // namespace details
+
+template <typename Span>
+class contiguous_span_iterator;
+template <typename Span>
+class general_span_iterator;
+
+template <std::ptrdiff_t DimSize = dynamic_range>
+struct dim_t
+{
+ static const std::ptrdiff_t value = DimSize;
+};
+template <>
+struct dim_t<dynamic_range>
+{
+ static const std::ptrdiff_t value = dynamic_range;
+ const std::ptrdiff_t dvalue;
+ constexpr dim_t(std::ptrdiff_t size) GSL_NOEXCEPT : dvalue(size) {}
+};
+
+template <std::ptrdiff_t N, class = std::enable_if_t<(N >= 0)>>
+constexpr dim_t<N> dim() GSL_NOEXCEPT
+{
+ return dim_t<N>();
+}
+
+template <std::ptrdiff_t N = dynamic_range, class = std::enable_if_t<N == dynamic_range>>
+constexpr dim_t<N> dim(std::ptrdiff_t n) GSL_NOEXCEPT
+{
+ return dim_t<>(n);
+}
+
+template <typename ValueType, std::ptrdiff_t FirstDimension = dynamic_range,
+ std::ptrdiff_t... RestDimensions>
+class multi_span;
+template <typename ValueType, std::size_t Rank>
+class strided_span;
+
+namespace details
+{
+ template <typename T, typename = std::true_type>
+ struct SpanTypeTraits
+ {
+ using value_type = T;
+ using size_type = std::size_t;
+ };
+
+ template <typename Traits>
+ struct SpanTypeTraits<Traits, typename std::is_reference<typename Traits::span_traits&>::type>
+ {
+ using value_type = typename Traits::span_traits::value_type;
+ using size_type = typename Traits::span_traits::size_type;
+ };
+
+ template <typename T, std::ptrdiff_t... Ranks>
+ struct SpanArrayTraits
+ {
+ using type = multi_span<T, Ranks...>;
+ using value_type = T;
+ using bounds_type = static_bounds<Ranks...>;
+ using pointer = T*;
+ using reference = T&;
+ };
+ template <typename T, std::ptrdiff_t N, std::ptrdiff_t... Ranks>
+ struct SpanArrayTraits<T[N], Ranks...> : SpanArrayTraits<T, Ranks..., N>
+ {
+ };
+
+ template <typename BoundsType>
+ BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size
+ {
+ Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX);
+ return BoundsType{totalSize};
+ }
+ template <typename BoundsType>
+ BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size
+ {
+ Expects(BoundsType::static_size <= totalSize);
+ return {};
+ }
+ template <typename BoundsType>
+ BoundsType newBoundsHelper(std::ptrdiff_t totalSize)
+ {
+ static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1");
+ return newBoundsHelperImpl<BoundsType>(
+ totalSize, std::integral_constant<bool, BoundsType::dynamic_rank == 1>());
+ }
+
+ struct Sep
+ {
+ };
+
+ template <typename T, typename... Args>
+ T static_as_multi_span_helper(Sep, Args... args)
+ {
+ return T{narrow_cast<typename T::size_type>(args)...};
+ }
+ template <typename T, typename Arg, typename... Args>
+ std::enable_if_t<
+ !std::is_same<Arg, dim_t<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T>
+ static_as_multi_span_helper(Arg, Args... args)
+ {
+ return static_as_multi_span_helper<T>(args...);
+ }
+ template <typename T, typename... Args>
+ T static_as_multi_span_helper(dim_t<dynamic_range> val, Args... args)
+ {
+ return static_as_multi_span_helper<T>(args..., val.dvalue);
+ }
+
+ template <typename... Dimensions>
+ struct static_as_multi_span_static_bounds_helper
+ {
+ using type = static_bounds<(Dimensions::value)...>;
+ };
+
+ template <typename T>
+ struct is_multi_span_oracle : std::false_type
+ {
+ };
+
+ template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
+ struct is_multi_span_oracle<multi_span<ValueType, FirstDimension, RestDimensions...>>
+ : std::true_type
+ {
+ };
+
+ template <typename ValueType, std::ptrdiff_t Rank>
+ struct is_multi_span_oracle<strided_span<ValueType, Rank>> : std::true_type
+ {
+ };
+
+ template <typename T>
+ struct is_multi_span : is_multi_span_oracle<std::remove_cv_t<T>>
+ {
+ };
+}
+
+template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
+class multi_span
+{
+ // TODO do we still need this?
+ template <typename ValueType2, std::ptrdiff_t FirstDimension2,
+ std::ptrdiff_t... RestDimensions2>
+ friend class multi_span;
+
+public:
+ using bounds_type = static_bounds<FirstDimension, RestDimensions...>;
+ static const std::size_t Rank = bounds_type::rank;
+ using size_type = typename bounds_type::size_type;
+ using index_type = typename bounds_type::index_type;
+ using value_type = ValueType;
+ using const_value_type = std::add_const_t<value_type>;
+ using pointer = std::add_pointer_t<value_type>;
+ using reference = std::add_lvalue_reference_t<value_type>;
+ using iterator = contiguous_span_iterator<multi_span>;
+ using const_span = multi_span<const_value_type, FirstDimension, RestDimensions...>;
+ using const_iterator = contiguous_span_iterator<const_span>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using sliced_type =
+ std::conditional_t<Rank == 1, value_type, multi_span<value_type, RestDimensions...>>;
+
+private:
+ pointer data_;
+ bounds_type bounds_;
+
+ friend iterator;
+ friend const_iterator;
+
+public:
+ // default constructor - same as constructing from nullptr_t
+ constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{})
+ {
+ static_assert(bounds_type::dynamic_rank != 0 ||
+ (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
+ "Default construction of multi_span<T> only possible "
+ "for dynamic or fixed, zero-length spans.");
+ }
+
+ // construct from nullptr - get an empty multi_span
+ constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{})
+ {
+ static_assert(bounds_type::dynamic_rank != 0 ||
+ (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
+ "nullptr_t construction of multi_span<T> only possible "
+ "for dynamic or fixed, zero-length spans.");
+ }
+
+ // construct from nullptr with size of 0 (helps with template function calls)
+ template <class IntType, typename = std::enable_if_t<std::is_integral<IntType>::value>>
+ constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT
+ : multi_span(nullptr, bounds_type{})
+ {
+ static_assert(bounds_type::dynamic_rank != 0 ||
+ (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
+ "nullptr_t construction of multi_span<T> only possible "
+ "for dynamic or fixed, zero-length spans.");
+ Expects(size == 0);
+ }
+
+ // construct from a single element
+ constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1})
+ {
+ static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 ||
+ bounds_type::static_size == 1,
+ "Construction from a single element only possible "
+ "for dynamic or fixed spans of length 0 or 1.");
+ }
+
+ // prevent constructing from temporaries for single-elements
+ constexpr multi_span(value_type&&) = delete;
+
+ // construct from pointer + length
+ constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT
+ : multi_span(ptr, bounds_type{size})
+ {
+ }
+
+ // construct from pointer + length - multidimensional
+ constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data),
+ bounds_(std::move(bounds))
+ {
+ Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0);
+ }
+
+ // construct from begin,end pointer pair
+ template <typename Ptr,
+ typename = std::enable_if_t<std::is_convertible<Ptr, pointer>::value &&
+ details::LessThan<bounds_type::dynamic_rank, 2>::value>>
+ constexpr multi_span(pointer begin, Ptr end)
+ : multi_span(begin,
+ details::newBoundsHelper<bounds_type>(static_cast<pointer>(end) - begin))
+ {
+ Expects(begin != nullptr && end != nullptr && begin <= static_cast<pointer>(end));
+ }
+
+ // construct from n-dimensions static array
+ template <typename T, std::size_t N, typename Helper = details::SpanArrayTraits<T, N>>
+ constexpr multi_span(T (&arr)[N])
+ : multi_span(reinterpret_cast<pointer>(arr), bounds_type{typename Helper::bounds_type{}})
+ {
+ static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
+ "Cannot convert from source type to target multi_span type.");
+ static_assert(std::is_convertible<typename Helper::bounds_type, bounds_type>::value,
+ "Cannot construct a multi_span from an array with fewer elements.");
+ }
+
+ // construct from n-dimensions dynamic array (e.g. new int[m][4])
+ // (precedence will be lower than the 1-dimension pointer)
+ template <typename T, typename Helper = details::SpanArrayTraits<T, dynamic_range>>
+ constexpr multi_span(T* const& data, size_type size)
+ : multi_span(reinterpret_cast<pointer>(data), typename Helper::bounds_type{size})
+ {
+ static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
+ "Cannot convert from source type to target multi_span type.");
+ }
+
+ // construct from std::array
+ template <typename T, std::size_t N>
+ constexpr multi_span(std::array<T, N>& arr)
+ : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
+ {
+ static_assert(
+ std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
+ "Cannot convert from source type to target multi_span type.");
+ static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
+ "You cannot construct a multi_span from a std::array of smaller size.");
+ }
+
+ // construct from const std::array
+ template <typename T, std::size_t N>
+ constexpr multi_span(const std::array<T, N>& arr)
+ : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
+ {
+ static_assert(std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
+ "Cannot convert from source type to target multi_span type.");
+ static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
+ "You cannot construct a multi_span from a std::array of smaller size.");
+ }
+
+ // prevent constructing from temporary std::array
+ template <typename T, std::size_t N>
+ constexpr multi_span(std::array<T, N>&& arr) = delete;
+
+ // construct from containers
+ // future: could use contiguous_iterator_traits to identify only contiguous containers
+ // type-requirements: container must have .size(), operator[] which are value_type compatible
+ template <typename Cont, typename DataType = typename Cont::value_type,
+ typename = std::enable_if_t<
+ !details::is_multi_span<Cont>::value &&
+ std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
+ *std::declval<Cont>().data())>,
+ DataType>::value>>
+ constexpr multi_span(Cont& cont)
+ : multi_span(static_cast<pointer>(cont.data()),
+ details::newBoundsHelper<bounds_type>(narrow_cast<size_type>(cont.size())))
+ {
+ }
+
+ // prevent constructing from temporary containers
+ template <typename Cont, typename DataType = typename Cont::value_type,
+ typename = std::enable_if_t<
+ !details::is_multi_span<Cont>::value &&
+ std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
+ *std::declval<Cont>().data())>,
+ DataType>::value>>
+ explicit constexpr multi_span(Cont&& cont) = delete;
+
+ // construct from a convertible multi_span
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename OtherBounds = static_bounds<OtherDimensions...>,
+ typename = std::enable_if_t<std::is_convertible<OtherValueType, ValueType>::value &&
+ std::is_convertible<OtherBounds, bounds_type>::value>>
+ constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other) GSL_NOEXCEPT
+ : data_(other.data_),
+ bounds_(other.bounds_)
+ {
+ }
+
+ // trivial copy and move
+ constexpr multi_span(const multi_span&) = default;
+ constexpr multi_span(multi_span&&) = default;
+
+ // trivial assignment
+ constexpr multi_span& operator=(const multi_span&) = default;
+ constexpr multi_span& operator=(multi_span&&) = default;
+
+ // first() - extract the first Count elements into a new multi_span
+ template <std::ptrdiff_t Count>
+ constexpr multi_span<ValueType, Count> first() const GSL_NOEXCEPT
+ {
+ static_assert(Count >= 0, "Count must be >= 0.");
+ static_assert(bounds_type::static_size == dynamic_range ||
+ Count <= bounds_type::static_size,
+ "Count is out of bounds.");
+
+ Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
+ return {this->data(), Count};
+ }
+
+ // first() - extract the first count elements into a new multi_span
+ constexpr multi_span<ValueType, dynamic_range> first(size_type count) const GSL_NOEXCEPT
+ {
+ Expects(count >= 0 && count <= this->size());
+ return {this->data(), count};
+ }
+
+ // last() - extract the last Count elements into a new multi_span
+ template <std::ptrdiff_t Count>
+ constexpr multi_span<ValueType, Count> last() const GSL_NOEXCEPT
+ {
+ static_assert(Count >= 0, "Count must be >= 0.");
+ static_assert(bounds_type::static_size == dynamic_range ||
+ Count <= bounds_type::static_size,
+ "Count is out of bounds.");
+
+ Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
+ return {this->data() + this->size() - Count, Count};
+ }
+
+ // last() - extract the last count elements into a new multi_span
+ constexpr multi_span<ValueType, dynamic_range> last(size_type count) const GSL_NOEXCEPT
+ {
+ Expects(count >= 0 && count <= this->size());
+ return {this->data() + this->size() - count, count};
+ }
+
+ // subspan() - create a subview of Count elements starting at Offset
+ template <std::ptrdiff_t Offset, std::ptrdiff_t Count>
+ constexpr multi_span<ValueType, Count> subspan() const GSL_NOEXCEPT
+ {
+ static_assert(Count >= 0, "Count must be >= 0.");
+ static_assert(Offset >= 0, "Offset must be >= 0.");
+ static_assert(bounds_type::static_size == dynamic_range ||
+ ((Offset <= bounds_type::static_size) &&
+ Count <= bounds_type::static_size - Offset),
+ "You must describe a sub-range within bounds of the multi_span.");
+
+ Expects(bounds_type::static_size != dynamic_range ||
+ (Offset <= this->size() && Count <= this->size() - Offset));
+ return {this->data() + Offset, Count};
+ }
+
+ // subspan() - create a subview of count elements starting at offset
+ // supplying dynamic_range for count will consume all available elements from offset
+ constexpr multi_span<ValueType, dynamic_range>
+ subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT
+ {
+ Expects((offset >= 0 && offset <= this->size()) &&
+ (count == dynamic_range || (count <= this->size() - offset)));
+ return {this->data() + offset, count == dynamic_range ? this->length() - offset : count};
+ }
+
+ // section - creates a non-contiguous, strided multi_span from a contiguous one
+ constexpr strided_span<ValueType, Rank> section(index_type origin,
+ index_type extents) const GSL_NOEXCEPT
+ {
+ size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
+ return {&this->operator[](origin), size,
+ strided_bounds<Rank>{extents, details::make_stride(bounds())}};
+ }
+
+ // length of the multi_span in elements
+ constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); }
+
+ // length of the multi_span in elements
+ constexpr size_type length() const GSL_NOEXCEPT { return this->size(); }
+
+ // length of the multi_span in bytes
+ constexpr size_type size_bytes() const GSL_NOEXCEPT
+ {
+ return narrow_cast<size_type>(sizeof(value_type)) * this->size();
+ }
+
+ // length of the multi_span in bytes
+ constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); }
+
+ constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; }
+
+ static constexpr std::size_t rank() { return Rank; }
+
+ template <std::size_t Dim = 0>
+ constexpr size_type extent() const GSL_NOEXCEPT
+ {
+ static_assert(Dim < Rank,
+ "Dimension should be less than rank (dimension count starts from 0).");
+ return bounds_.template extent<Dim>();
+ }
+
+ template <typename IntType>
+ constexpr size_type extent(IntType dim) const GSL_NOEXCEPT
+ {
+ return bounds_.extent(dim);
+ }
+
+ constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; }
+
+ constexpr pointer data() const GSL_NOEXCEPT { return data_; }
+
+ template <typename FirstIndex>
+ constexpr reference operator()(FirstIndex idx)
+ {
+ return this->operator[](narrow_cast<std::ptrdiff_t>(idx));
+ }
+
+ template <typename FirstIndex, typename... OtherIndices>
+ constexpr reference operator()(FirstIndex firstIndex, OtherIndices... indices)
+ {
+ index_type idx = {narrow_cast<std::ptrdiff_t>(firstIndex),
+ narrow_cast<std::ptrdiff_t>(indices)...};
+ return this->operator[](idx);
+ }
+
+ constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT
+ {
+ return data_[bounds_.linearize(idx)];
+ }
+
+ template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
+ constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT
+ {
+ Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array
+ const size_type ridx = idx * bounds_.stride();
+
+ // index is out of bounds of the underlying data
+ Expects(ridx < bounds_.total_size());
+ return Ret{data_ + ridx, bounds_.slice()};
+ }
+
+ constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; }
+
+ constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; }
+
+ constexpr const_iterator cbegin() const GSL_NOEXCEPT
+ {
+ return const_iterator{reinterpret_cast<const const_span*>(this), true};
+ }
+
+ constexpr const_iterator cend() const GSL_NOEXCEPT
+ {
+ return const_iterator{reinterpret_cast<const const_span*>(this), false};
+ }
+
+ constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; }
+
+ constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; }
+
+ constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT
+ {
+ return const_reverse_iterator{cend()};
+ }
+
+ constexpr const_reverse_iterator crend() const GSL_NOEXCEPT
+ {
+ return const_reverse_iterator{cbegin()};
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
+ {
+ return bounds_.size() == other.bounds_.size() &&
+ (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
+ {
+ return !(*this == other);
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
+ {
+ return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
+ {
+ return !(other < *this);
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
+ {
+ return (other < *this);
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
+ {
+ return !(*this < other);
+ }
+};
+
+//
+// Free functions for manipulating spans
+//
+
+// reshape a multi_span into a different dimensionality
+// DimCount and Enabled here are workarounds for a bug in MSVC 2015
+template <typename SpanType, typename... Dimensions2, std::size_t DimCount = sizeof...(Dimensions2),
+ bool Enabled = (DimCount > 0), typename = std::enable_if_t<Enabled>>
+constexpr auto as_multi_span(SpanType s, Dimensions2... dims)
+ -> multi_span<typename SpanType::value_type, Dimensions2::value...>
+{
+ static_assert(details::is_multi_span<SpanType>::value,
+ "Variadic as_multi_span() is for reshaping existing spans.");
+ using BoundsType =
+ typename multi_span<typename SpanType::value_type, (Dimensions2::value)...>::bounds_type;
+ auto tobounds = details::static_as_multi_span_helper<BoundsType>(dims..., details::Sep{});
+ details::verifyBoundsReshape(s.bounds(), tobounds);
+ return {s.data(), tobounds};
+}
+
+// convert a multi_span<T> to a multi_span<const byte>
+template <typename U, std::ptrdiff_t... Dimensions>
+multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT
+{
+ static_assert(std::is_trivial<std::decay_t<U>>::value,
+ "The value_type of multi_span must be a trivial type.");
+ return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
+}
+
+// convert a multi_span<T> to a multi_span<byte> (a writeable byte multi_span)
+// this is not currently a portable function that can be relied upon to work
+// on all implementations. It should be considered an experimental extension
+// to the standard GSL interface.
+template <typename U, std::ptrdiff_t... Dimensions>
+multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT
+{
+ static_assert(std::is_trivial<std::decay_t<U>>::value,
+ "The value_type of multi_span must be a trivial type.");
+ return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
+}
+
+// convert a multi_span<const byte> to a multi_span<const T>
+// this is not currently a portable function that can be relied upon to work
+// on all implementations. It should be considered an experimental extension
+// to the standard GSL interface.
+template <typename U, std::ptrdiff_t... Dimensions>
+constexpr auto
+as_multi_span(multi_span<const byte, Dimensions...> s) GSL_NOEXCEPT -> multi_span<
+ const U, static_cast<std::ptrdiff_t>(
+ multi_span<const byte, Dimensions...>::bounds_type::static_size != dynamic_range
+ ? (static_cast<std::size_t>(
+ multi_span<const byte, Dimensions...>::bounds_type::static_size) /
+ sizeof(U))
+ : dynamic_range)>
+{
+ using ConstByteSpan = multi_span<const byte, Dimensions...>;
+ static_assert(
+ std::is_trivial<std::decay_t<U>>::value &&
+ (ConstByteSpan::bounds_type::static_size == dynamic_range ||
+ ConstByteSpan::bounds_type::static_size % narrow_cast<std::ptrdiff_t>(sizeof(U)) == 0),
+ "Target type must be a trivial type and its size must match the byte array size");
+
+ Expects((s.size_bytes() % narrow_cast<std::ptrdiff_t>(sizeof(U))) == 0 &&
+ (s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))) < PTRDIFF_MAX);
+ return {reinterpret_cast<const U*>(s.data()),
+ s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
+}
+
+// convert a multi_span<byte> to a multi_span<T>
+// this is not currently a portable function that can be relied upon to work
+// on all implementations. It should be considered an experimental extension
+// to the standard GSL interface.
+template <typename U, std::ptrdiff_t... Dimensions>
+constexpr auto as_multi_span(multi_span<byte, Dimensions...> s) GSL_NOEXCEPT
+ -> multi_span<U, narrow_cast<std::ptrdiff_t>(
+ multi_span<byte, Dimensions...>::bounds_type::static_size != dynamic_range
+ ? static_cast<std::size_t>(
+ multi_span<byte, Dimensions...>::bounds_type::static_size) /
+ sizeof(U)
+ : dynamic_range)>
+{
+ using ByteSpan = multi_span<byte, Dimensions...>;
+ static_assert(
+ std::is_trivial<std::decay_t<U>>::value &&
+ (ByteSpan::bounds_type::static_size == dynamic_range ||
+ ByteSpan::bounds_type::static_size % sizeof(U) == 0),
+ "Target type must be a trivial type and its size must match the byte array size");
+
+ Expects((s.size_bytes() % sizeof(U)) == 0);
+ return {reinterpret_cast<U*>(s.data()),
+ s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
+}
+
+template <typename T, std::ptrdiff_t... Dimensions>
+constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args)
+ -> multi_span<std::remove_all_extents_t<T>, Dimensions...>
+{
+ return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr),
+ details::static_as_multi_span_helper<static_bounds<Dimensions...>>(args...,
+ details::Sep{})};
+}
+
+template <typename T>
+constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) ->
+ typename details::SpanArrayTraits<T, dynamic_range>::type
+{
+ return {reinterpret_cast<std::remove_all_extents_t<T>*>(arr), len};
+}
+
+template <typename T, std::size_t N>
+constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type
+{
+ return {arr};
+}
+
+template <typename T, std::size_t N>
+constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr)
+{
+ return {arr};
+}
+
+template <typename T, std::size_t N>
+constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete;
+
+template <typename T, std::size_t N>
+constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr)
+{
+ return {arr};
+}
+
+template <typename T>
+constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end)
+{
+ return {begin, end};
+}
+
+template <typename Cont>
+constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t<
+ !details::is_multi_span<std::decay_t<Cont>>::value,
+ multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
+{
+ Expects(arr.size() < PTRDIFF_MAX);
+ return {arr.data(), narrow_cast<std::ptrdiff_t>(arr.size())};
+}
+
+template <typename Cont>
+constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t<
+ !details::is_multi_span<std::decay_t<Cont>>::value,
+ multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete;
+
+// from basic_string which doesn't have nonconst .data() member like other contiguous containers
+template <typename CharT, typename Traits, typename Allocator>
+constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str)
+ -> multi_span<CharT, dynamic_range>
+{
+ Expects(str.size() < PTRDIFF_MAX);
+ return {&str[0], narrow_cast<std::ptrdiff_t>(str.size())};
+}
+
+// strided_span is an extension that is not strictly part of the GSL at this time.
+// It is kept here while the multidimensional interface is still being defined.
+template <typename ValueType, std::size_t Rank>
+class strided_span
+{
+public:
+ using bounds_type = strided_bounds<Rank>;
+ using size_type = typename bounds_type::size_type;
+ using index_type = typename bounds_type::index_type;
+ using value_type = ValueType;
+ using const_value_type = std::add_const_t<value_type>;
+ using pointer = std::add_pointer_t<value_type>;
+ using reference = std::add_lvalue_reference_t<value_type>;
+ using iterator = general_span_iterator<strided_span>;
+ using const_strided_span = strided_span<const_value_type, Rank>;
+ using const_iterator = general_span_iterator<const_strided_span>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using sliced_type =
+ std::conditional_t<Rank == 1, value_type, strided_span<value_type, Rank - 1>>;
+
+private:
+ pointer data_;
+ bounds_type bounds_;
+
+ friend iterator;
+ friend const_iterator;
+ template <typename OtherValueType, std::size_t OtherRank>
+ friend class strided_span;
+
+public:
+ // from raw data
+ constexpr strided_span(pointer ptr, size_type size, bounds_type bounds)
+ : data_(ptr), bounds_(std::move(bounds))
+ {
+ Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0);
+ // Bounds cross data boundaries
+ Expects(this->bounds().total_size() <= size);
+ (void) size;
+ }
+
+ // from static array of size N
+ template <size_type N>
+ constexpr strided_span(value_type (&values)[N], bounds_type bounds)
+ : strided_span(values, N, std::move(bounds))
+ {
+ }
+
+ // from array view
+ template <typename OtherValueType, std::ptrdiff_t... Dimensions,
+ bool Enabled1 = (sizeof...(Dimensions) == Rank),
+ bool Enabled2 = std::is_convertible<OtherValueType*, ValueType*>::value,
+ typename = std::enable_if_t<Enabled1 && Enabled2>>
+ constexpr strided_span(multi_span<OtherValueType, Dimensions...> av, bounds_type bounds)
+ : strided_span(av.data(), av.bounds().total_size(), std::move(bounds))
+ {
+ }
+
+ // convertible
+ template <typename OtherValueType, typename = std::enable_if_t<std::is_convertible<
+ OtherValueType (*)[], value_type (*)[]>::value>>
+ constexpr strided_span(const strided_span<OtherValueType, Rank>& other)
+ : data_(other.data_), bounds_(other.bounds_)
+ {
+ }
+
+ // convert from bytes
+ template <typename OtherValueType>
+ constexpr strided_span<
+ typename std::enable_if<std::is_same<value_type, const byte>::value, OtherValueType>::type,
+ Rank>
+ as_strided_span() const
+ {
+ static_assert((sizeof(OtherValueType) >= sizeof(value_type)) &&
+ (sizeof(OtherValueType) % sizeof(value_type) == 0),
+ "OtherValueType should have a size to contain a multiple of ValueTypes");
+ auto d = narrow_cast<size_type>(sizeof(OtherValueType) / sizeof(value_type));
+
+ size_type size = this->bounds().total_size() / d;
+ return {const_cast<OtherValueType*>(reinterpret_cast<const OtherValueType*>(this->data())),
+ size,
+ bounds_type{resize_extent(this->bounds().index_bounds(), d),
+ resize_stride(this->bounds().strides(), d)}};
+ }
+
+ constexpr strided_span section(index_type origin, index_type extents) const
+ {
+ size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
+ return {&this->operator[](origin), size,
+ bounds_type{extents, details::make_stride(bounds())}};
+ }
+
+ constexpr reference operator[](const index_type& idx) const
+ {
+ return data_[bounds_.linearize(idx)];
+ }
+
+ template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
+ constexpr Ret operator[](size_type idx) const
+ {
+ Expects(idx < bounds_.size()); // index is out of bounds of the array
+ const size_type ridx = idx * bounds_.stride();
+
+ // index is out of bounds of the underlying data
+ Expects(ridx < bounds_.total_size());
+ return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()};
+ }
+
+ constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; }
+
+ template <std::size_t Dim = 0>
+ constexpr size_type extent() const GSL_NOEXCEPT
+ {
+ static_assert(Dim < Rank,
+ "dimension should be less than Rank (dimension count starts from 0)");
+ return bounds_.template extent<Dim>();
+ }
+
+ constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); }
+
+ constexpr pointer data() const GSL_NOEXCEPT { return data_; }
+
+ constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; }
+
+ constexpr iterator begin() const { return iterator{this, true}; }
+
+ constexpr iterator end() const { return iterator{this, false}; }
+
+ constexpr const_iterator cbegin() const
+ {
+ return const_iterator{reinterpret_cast<const const_strided_span*>(this), true};
+ }
+
+ constexpr const_iterator cend() const
+ {
+ return const_iterator{reinterpret_cast<const const_strided_span*>(this), false};
+ }
+
+ constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+
+ constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; }
+
+ constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; }
+
+ constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; }
+
+ template <typename OtherValueType, std::ptrdiff_t OtherRank,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator==(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
+ {
+ return bounds_.size() == other.bounds_.size() &&
+ (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t OtherRank,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator!=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
+ {
+ return !(*this == other);
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t OtherRank,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator<(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
+ {
+ return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t OtherRank,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator<=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
+ {
+ return !(other < *this);
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t OtherRank,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator>(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
+ {
+ return (other < *this);
+ }
+
+ template <typename OtherValueType, std::ptrdiff_t OtherRank,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ constexpr bool
+ operator>=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
+ {
+ return !(*this < other);
+ }
+
+private:
+ static index_type resize_extent(const index_type& extent, std::ptrdiff_t d)
+ {
+ // The last dimension of the array needs to contain a multiple of new type elements
+ Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0));
+
+ index_type ret = extent;
+ ret[Rank - 1] /= d;
+
+ return ret;
+ }
+
+ template <bool Enabled = (Rank == 1), typename = std::enable_if_t<Enabled>>
+ static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr)
+ {
+ // Only strided arrays with regular strides can be resized
+ Expects(strides[Rank - 1] == 1);
+
+ return strides;
+ }
+
+ template <bool Enabled = (Rank > 1), typename = std::enable_if_t<Enabled>>
+ static index_type resize_stride(const index_type& strides, std::ptrdiff_t d)
+ {
+ // Only strided arrays with regular strides can be resized
+ Expects(strides[Rank - 1] == 1);
+ // The strides must have contiguous chunks of
+ // memory that can contain a multiple of new type elements
+ Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0));
+
+ for (std::size_t i = Rank - 1; i > 0; --i) {
+ // Only strided arrays with regular strides can be resized
+ Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0));
+ }
+
+ index_type ret = strides / d;
+ ret[Rank - 1] = 1;
+
+ return ret;
+ }
+};
+
+template <class Span>
+class contiguous_span_iterator
+{
+public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = typename Span::value_type;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+private:
+ template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
+ friend class multi_span;
+
+ pointer data_;
+ const Span* m_validator;
+ void validateThis() const
+ {
+ // iterator is out of range of the array
+ Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size());
+ }
+ contiguous_span_iterator(const Span* container, bool isbegin)
+ : data_(isbegin ? container->data_ : container->data_ + container->size())
+ , m_validator(container)
+ {
+ }
+
+public:
+ reference operator*() const GSL_NOEXCEPT
+ {
+ validateThis();
+ return *data_;
+ }
+ pointer operator->() const GSL_NOEXCEPT
+ {
+ validateThis();
+ return data_;
+ }
+ contiguous_span_iterator& operator++() GSL_NOEXCEPT
+ {
+ ++data_;
+ return *this;
+ }
+ contiguous_span_iterator operator++(int) GSL_NOEXCEPT
+ {
+ auto ret = *this;
+ ++(*this);
+ return ret;
+ }
+ contiguous_span_iterator& operator--() GSL_NOEXCEPT
+ {
+ --data_;
+ return *this;
+ }
+ contiguous_span_iterator operator--(int) GSL_NOEXCEPT
+ {
+ auto ret = *this;
+ --(*this);
+ return ret;
+ }
+ contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT
+ {
+ contiguous_span_iterator ret{*this};
+ return ret += n;
+ }
+ contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
+ {
+ data_ += n;
+ return *this;
+ }
+ contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT
+ {
+ contiguous_span_iterator ret{*this};
+ return ret -= n;
+ }
+ contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
+ difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ Expects(m_validator == rhs.m_validator);
+ return data_ - rhs.data_;
+ }
+ reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); }
+ bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ Expects(m_validator == rhs.m_validator);
+ return data_ == rhs.data_;
+ }
+ bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return !(*this == rhs);
+ }
+ bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ Expects(m_validator == rhs.m_validator);
+ return data_ < rhs.data_;
+ }
+ bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return !(rhs < *this);
+ }
+ bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
+ bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ return !(rhs > *this);
+ }
+ void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT
+ {
+ std::swap(data_, rhs.data_);
+ std::swap(m_validator, rhs.m_validator);
+ }
+};
+
+template <typename Span>
+contiguous_span_iterator<Span> operator+(typename contiguous_span_iterator<Span>::difference_type n,
+ const contiguous_span_iterator<Span>& rhs) GSL_NOEXCEPT
+{
+ return rhs + n;
+}
+
+template <typename Span>
+class general_span_iterator
+{
+public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = typename Span::value_type;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+private:
+ template <typename ValueType, std::size_t Rank>
+ friend class strided_span;
+
+ const Span* m_container;
+ typename Span::bounds_type::iterator m_itr;
+ general_span_iterator(const Span* container, bool isbegin)
+ : m_container(container)
+ , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end())
+ {
+ }
+
+public:
+ reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; }
+ pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; }
+ general_span_iterator& operator++() GSL_NOEXCEPT
+ {
+ ++m_itr;
+ return *this;
+ }
+ general_span_iterator operator++(int) GSL_NOEXCEPT
+ {
+ auto ret = *this;
+ ++(*this);
+ return ret;
+ }
+ general_span_iterator& operator--() GSL_NOEXCEPT
+ {
+ --m_itr;
+ return *this;
+ }
+ general_span_iterator operator--(int) GSL_NOEXCEPT
+ {
+ auto ret = *this;
+ --(*this);
+ return ret;
+ }
+ general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT
+ {
+ general_span_iterator ret{*this};
+ return ret += n;
+ }
+ general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
+ {
+ m_itr += n;
+ return *this;
+ }
+ general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT
+ {
+ general_span_iterator ret{*this};
+ return ret -= n;
+ }
+ general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
+ difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ Expects(m_container == rhs.m_container);
+ return m_itr - rhs.m_itr;
+ }
+ value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; }
+
+ bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ Expects(m_container == rhs.m_container);
+ return m_itr == rhs.m_itr;
+ }
+ bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); }
+ bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT
+ {
+ Expects(m_container == rhs.m_container);
+ return m_itr < rhs.m_itr;
+ }
+ bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); }
+ bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
+ bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); }
+ void swap(general_span_iterator& rhs) GSL_NOEXCEPT
+ {
+ std::swap(m_itr, rhs.m_itr);
+ std::swap(m_container, rhs.m_container);
+ }
+};
+
+template <typename Span>
+general_span_iterator<Span> operator+(typename general_span_iterator<Span>::difference_type n,
+ const general_span_iterator<Span>& rhs) GSL_NOEXCEPT
+{
+ return rhs + n;
+}
+
+} // namespace gsl
+
+#undef GSL_NOEXCEPT
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1910
+
+#undef constexpr
+#pragma pop_macro("constexpr")
+#endif // _MSC_VER < 1910
+
+#pragma warning(pop)
+
+#endif // _MSC_VER
+
+#if __GNUC__ > 6
+#pragma GCC diagnostic pop
+#endif // __GNUC__ > 6
+
+#endif // GSL_MULTI_SPAN_H
diff --git a/third_party/GSL/include/gsl/pointers b/third_party/GSL/include/gsl/pointers
new file mode 100644
index 0000000..69499d6
--- /dev/null
+++ b/third_party/GSL/include/gsl/pointers
@@ -0,0 +1,193 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_POINTERS_H
+#define GSL_POINTERS_H
+
+#include <gsl/gsl_assert> // for Ensures, Expects
+
+#include <algorithm> // for forward
+#include <iosfwd> // for ptrdiff_t, nullptr_t, ostream, size_t
+#include <memory> // for shared_ptr, unique_ptr
+#include <system_error> // for hash
+#include <type_traits> // for enable_if_t, is_convertible, is_assignable
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+
+#endif // defined(_MSC_VER) && _MSC_VER < 1910
+
+namespace gsl
+{
+
+//
+// GSL.owner: ownership pointers
+//
+using std::unique_ptr;
+using std::shared_ptr;
+
+//
+// owner
+//
+// owner<T> is designed as a bridge for code that must deal directly with owning pointers for some reason
+//
+// T must be a pointer type
+// - disallow construction from any type other than pointer type
+//
+template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
+using owner = T;
+
+//
+// not_null
+//
+// Restricts a pointer or smart pointer to only hold non-null values.
+//
+// Has zero size overhead over T.
+//
+// If T is a pointer (i.e. T == U*) then
+// - allow construction from U*
+// - disallow construction from nullptr_t
+// - disallow default construction
+// - ensure construction from null U* fails
+// - allow implicit conversion to U*
+//
+template <class T>
+class not_null
+{
+public:
+ static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
+
+ template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+ constexpr explicit not_null(U&& u) : ptr_(std::forward<U>(u))
+ {
+ Expects(ptr_ != nullptr);
+ }
+
+ template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
+ constexpr explicit not_null(T u) : ptr_(u)
+ {
+ Expects(ptr_ != nullptr);
+ }
+
+ template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+ constexpr not_null(const not_null<U>& other) : not_null(other.get())
+ {
+ }
+
+ not_null(not_null&& other) = default;
+ not_null(const not_null& other) = default;
+ not_null& operator=(const not_null& other) = default;
+
+ constexpr T get() const
+ {
+ Ensures(ptr_ != nullptr);
+ return ptr_;
+ }
+
+ constexpr operator T() const { return get(); }
+ constexpr T operator->() const { return get(); }
+ constexpr decltype(auto) operator*() const { return *get(); }
+
+ // prevents compilation when someone attempts to assign a null pointer constant
+ not_null(std::nullptr_t) = delete;
+ not_null& operator=(std::nullptr_t) = delete;
+
+ // unwanted operators...pointers only point to single objects!
+ not_null& operator++() = delete;
+ not_null& operator--() = delete;
+ not_null operator++(int) = delete;
+ not_null operator--(int) = delete;
+ not_null& operator+=(std::ptrdiff_t) = delete;
+ not_null& operator-=(std::ptrdiff_t) = delete;
+ void operator[](std::ptrdiff_t) const = delete;
+
+private:
+ T ptr_;
+};
+
+template <class T>
+std::ostream& operator<<(std::ostream& os, const not_null<T>& val)
+{
+ os << val.get();
+ return os;
+}
+
+template <class T, class U>
+auto operator==(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() == rhs.get())
+{
+ return lhs.get() == rhs.get();
+}
+
+template <class T, class U>
+auto operator!=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() != rhs.get())
+{
+ return lhs.get() != rhs.get();
+}
+
+template <class T, class U>
+auto operator<(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() < rhs.get())
+{
+ return lhs.get() < rhs.get();
+}
+
+template <class T, class U>
+auto operator<=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() <= rhs.get())
+{
+ return lhs.get() <= rhs.get();
+}
+
+template <class T, class U>
+auto operator>(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() > rhs.get())
+{
+ return lhs.get() > rhs.get();
+}
+
+template <class T, class U>
+auto operator>=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() >= rhs.get())
+{
+ return lhs.get() >= rhs.get();
+}
+
+// more unwanted operators
+template <class T, class U>
+std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
+template <class T>
+not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
+template <class T>
+not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
+template <class T>
+not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
+
+} // namespace gsl
+
+namespace std
+{
+template <class T>
+struct hash<gsl::not_null<T>>
+{
+ std::size_t operator()(const gsl::not_null<T>& value) const { return hash<T>{}(value); }
+};
+
+} // namespace std
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // defined(_MSC_VER) && _MSC_VER < 1910
+
+#endif // GSL_POINTERS_H
diff --git a/third_party/GSL/include/gsl/span b/third_party/GSL/include/gsl/span
new file mode 100644
index 0000000..2fa9cc5
--- /dev/null
+++ b/third_party/GSL/include/gsl/span
@@ -0,0 +1,766 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_SPAN_H
+#define GSL_SPAN_H
+
+#include <gsl/gsl_assert> // for Expects
+#include <gsl/gsl_byte> // for byte
+#include <gsl/gsl_util> // for narrow_cast, narrow
+
+#include <algorithm> // for lexicographical_compare
+#include <array> // for array
+#include <cstddef> // for ptrdiff_t, size_t, nullptr_t
+#include <iterator> // for reverse_iterator, distance, random_access_...
+#include <limits>
+#include <stdexcept>
+#include <type_traits> // for enable_if_t, declval, is_convertible, inte...
+#include <utility>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+
+// turn off some warnings that are noisy about our Expects statements
+#pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4702) // unreachable code
+
+// 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*/
+#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND
+
+#endif // _MSC_VER < 1910
+#else // _MSC_VER
+
+// See if we have enough C++17 power to use a static constexpr data member
+// without needing an out-of-line definition
+#if !(defined(__cplusplus) && (__cplusplus >= 201703L))
+#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND
+#endif // !(defined(__cplusplus) && (__cplusplus >= 201703L))
+
+#endif // _MSC_VER
+
+// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t)
+// While there is a conversion from signed to unsigned, it happens at
+// compiletime, so the compiler wouldn't have to warn indiscriminently, but
+// could check if the source value actually doesn't fit into the target type
+// and only warn in those cases.
+#if __GNUC__ > 6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+namespace gsl
+{
+
+// [views.constants], constants
+constexpr const std::ptrdiff_t dynamic_extent = -1;
+
+template <class ElementType, std::ptrdiff_t Extent = dynamic_extent>
+class span;
+
+// implementation details
+namespace details
+{
+ template <class T>
+ struct is_span_oracle : std::false_type
+ {
+ };
+
+ template <class ElementType, std::ptrdiff_t Extent>
+ struct is_span_oracle<gsl::span<ElementType, Extent>> : std::true_type
+ {
+ };
+
+ template <class T>
+ struct is_span : public is_span_oracle<std::remove_cv_t<T>>
+ {
+ };
+
+ template <class T>
+ struct is_std_array_oracle : std::false_type
+ {
+ };
+
+ template <class ElementType, std::size_t Extent>
+ struct is_std_array_oracle<std::array<ElementType, Extent>> : std::true_type
+ {
+ };
+
+ template <class T>
+ struct is_std_array : public is_std_array_oracle<std::remove_cv_t<T>>
+ {
+ };
+
+ template <std::ptrdiff_t From, std::ptrdiff_t To>
+ struct is_allowed_extent_conversion
+ : public std::integral_constant<bool, From == To || From == gsl::dynamic_extent ||
+ To == gsl::dynamic_extent>
+ {
+ };
+
+ template <class From, class To>
+ struct is_allowed_element_type_conversion
+ : public std::integral_constant<bool, std::is_convertible<From (*)[], To (*)[]>::value>
+ {
+ };
+
+ template <class Span, bool IsConst>
+ class span_iterator
+ {
+ using element_type_ = typename Span::element_type;
+
+ public:
+
+#ifdef _MSC_VER
+ // Tell Microsoft standard library that span_iterators are checked.
+ using _Unchecked_type = typename Span::pointer;
+#endif
+
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = std::remove_cv_t<element_type_>;
+ using difference_type = typename Span::index_type;
+
+ using reference = std::conditional_t<IsConst, const element_type_, element_type_>&;
+ using pointer = std::add_pointer_t<reference>;
+
+ span_iterator() = default;
+
+ constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept
+ : span_(span), index_(idx)
+ {}
+
+ friend span_iterator<Span, true>;
+ template<bool B, std::enable_if_t<!B && IsConst>* = nullptr>
+ constexpr span_iterator(const span_iterator<Span, B>& other) noexcept
+ : span_iterator(other.span_, other.index_)
+ {
+ }
+
+ constexpr reference operator*() const
+ {
+ Expects(index_ != span_->size());
+ return *(span_->data() + index_);
+ }
+
+ constexpr pointer operator->() const
+ {
+ Expects(index_ != span_->size());
+ return span_->data() + index_;
+ }
+
+ constexpr span_iterator& operator++()
+ {
+ Expects(0 <= index_ && index_ != span_->size());
+ ++index_;
+ return *this;
+ }
+
+ constexpr span_iterator operator++(int)
+ {
+ auto ret = *this;
+ ++(*this);
+ return ret;
+ }
+
+ constexpr span_iterator& operator--()
+ {
+ Expects(index_ != 0 && index_ <= span_->size());
+ --index_;
+ return *this;
+ }
+
+ constexpr span_iterator operator--(int)
+ {
+ auto ret = *this;
+ --(*this);
+ return ret;
+ }
+
+ constexpr span_iterator operator+(difference_type n) const
+ {
+ auto ret = *this;
+ return ret += n;
+ }
+
+ friend constexpr span_iterator operator+(difference_type n, span_iterator const& rhs)
+ {
+ return rhs + n;
+ }
+
+ constexpr span_iterator& operator+=(difference_type n)
+ {
+ Expects((index_ + n) >= 0 && (index_ + n) <= span_->size());
+ index_ += n;
+ return *this;
+ }
+
+ constexpr span_iterator operator-(difference_type n) const
+ {
+ auto ret = *this;
+ return ret -= n;
+ }
+
+ constexpr span_iterator& operator-=(difference_type n) { return *this += -n; }
+
+ constexpr difference_type operator-(span_iterator rhs) const
+ {
+ Expects(span_ == rhs.span_);
+ return index_ - rhs.index_;
+ }
+
+ constexpr reference operator[](difference_type n) const
+ {
+ return *(*this + n);
+ }
+
+ constexpr friend bool operator==(span_iterator lhs,
+ span_iterator rhs) noexcept
+ {
+ return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_;
+ }
+
+ constexpr friend bool operator!=(span_iterator lhs,
+ span_iterator rhs) noexcept
+ {
+ return !(lhs == rhs);
+ }
+
+ constexpr friend bool operator<(span_iterator lhs,
+ span_iterator rhs) noexcept
+ {
+ return lhs.index_ < rhs.index_;
+ }
+
+ constexpr friend bool operator<=(span_iterator lhs,
+ span_iterator rhs) noexcept
+ {
+ return !(rhs < lhs);
+ }
+
+ constexpr friend bool operator>(span_iterator lhs,
+ span_iterator rhs) noexcept
+ {
+ return rhs < lhs;
+ }
+
+ constexpr friend bool operator>=(span_iterator lhs,
+ span_iterator rhs) noexcept
+ {
+ return !(rhs > lhs);
+ }
+
+#ifdef _MSC_VER
+ // MSVC++ iterator debugging support; allows STL algorithms in 15.8+
+ // to unwrap span_iterator to a pointer type after a range check in STL
+ // algorithm calls
+ friend constexpr void _Verify_range(span_iterator lhs,
+ span_iterator rhs) noexcept
+ { // test that [lhs, rhs) forms a valid range inside an STL algorithm
+ Expects(lhs.span_ == rhs.span_ // range spans have to match
+ && lhs.index_ <= rhs.index_); // range must not be transposed
+ }
+
+ constexpr void _Verify_offset(const difference_type n) const noexcept
+ { // test that the iterator *this + n is a valid range in an STL
+ // algorithm call
+ Expects((index_ + n) >= 0 && (index_ + n) <= span_->size());
+ }
+
+ constexpr pointer _Unwrapped() const noexcept
+ { // after seeking *this to a high water mark, or using one of the
+ // _Verify_xxx functions above, unwrap this span_iterator to a raw
+ // pointer
+ return span_->data() + index_;
+ }
+
+ // Tell the STL that span_iterator should not be unwrapped if it can't
+ // validate in advance, even in release / optimized builds:
+#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND)
+ static constexpr const bool _Unwrap_when_unverified = false;
+#else
+ static constexpr bool _Unwrap_when_unverified = false;
+#endif
+ constexpr void _Seek_to(const pointer p) noexcept
+ { // adjust the position of *this to previously verified location p
+ // after _Unwrapped
+ index_ = p - span_->data();
+ }
+#endif
+
+ protected:
+ const Span* span_ = nullptr;
+ std::ptrdiff_t index_ = 0;
+ };
+
+ template <std::ptrdiff_t Ext>
+ class extent_type
+ {
+ public:
+ using index_type = std::ptrdiff_t;
+
+ static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size.");
+
+ constexpr extent_type() noexcept {}
+
+ template <index_type Other>
+ constexpr extent_type(extent_type<Other> ext)
+ {
+ static_assert(Other == Ext || Other == dynamic_extent,
+ "Mismatch between fixed-size extent and size of initializing data.");
+ Expects(ext.size() == Ext);
+ }
+
+ constexpr extent_type(index_type size) { Expects(size == Ext); }
+
+ constexpr index_type size() const noexcept { return Ext; }
+ };
+
+ template <>
+ class extent_type<dynamic_extent>
+ {
+ public:
+ using index_type = std::ptrdiff_t;
+
+ template <index_type Other>
+ explicit constexpr extent_type(extent_type<Other> ext) : size_(ext.size())
+ {
+ }
+
+ explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); }
+
+ constexpr index_type size() const noexcept { return size_; }
+
+ private:
+ index_type size_;
+ };
+
+ template <class ElementType, std::ptrdiff_t Extent, std::ptrdiff_t Offset, std::ptrdiff_t Count>
+ struct calculate_subspan_type
+ {
+ using type = span<ElementType, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : Extent)>;
+ };
+} // namespace details
+
+// [span], class template span
+template <class ElementType, std::ptrdiff_t Extent>
+class span
+{
+public:
+ // constants and types
+ using element_type = ElementType;
+ using value_type = std::remove_cv_t<ElementType>;
+ using index_type = std::ptrdiff_t;
+ using pointer = element_type*;
+ using reference = element_type&;
+
+ using iterator = details::span_iterator<span<ElementType, Extent>, false>;
+ using const_iterator = details::span_iterator<span<ElementType, Extent>, true>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ using size_type = index_type;
+
+#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND)
+ static constexpr const index_type extent { Extent };
+#else
+ static constexpr index_type extent { Extent };
+#endif
+
+ // [span.cons], span constructors, copy, assignment, and destructor
+ template <bool Dependent = false,
+ // "Dependent" is needed to make "std::enable_if_t<Dependent || Extent <= 0>" SFINAE,
+ // since "std::enable_if_t<Extent <= 0>" is ill-formed when Extent is greater than 0.
+ class = std::enable_if_t<(Dependent || Extent <= 0)>>
+ constexpr span() noexcept : storage_(nullptr, details::extent_type<0>())
+ {
+ }
+
+ constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {}
+
+ constexpr span(pointer firstElem, pointer lastElem)
+ : storage_(firstElem, std::distance(firstElem, lastElem))
+ {
+ }
+
+ template <std::size_t N>
+ constexpr span(element_type (&arr)[N]) noexcept
+ : storage_(KnownNotNull{&arr[0]}, details::extent_type<N>())
+ {
+ }
+
+ template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
+ constexpr span(std::array<ArrayElementType, N>& arr) noexcept
+ : storage_(&arr[0], details::extent_type<N>())
+ {
+ }
+
+ template <std::size_t N>
+ constexpr span(const std::array<std::remove_const_t<element_type>, N>& arr) noexcept
+ : storage_(&arr[0], details::extent_type<N>())
+ {
+ }
+
+ // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement
+ // on Container to be a contiguous sequence container.
+ template <class Container,
+ class = std::enable_if_t<
+ !details::is_span<Container>::value && !details::is_std_array<Container>::value &&
+ std::is_convertible<typename Container::pointer, pointer>::value &&
+ std::is_convertible<typename Container::pointer,
+ decltype(std::declval<Container>().data())>::value>>
+ constexpr span(Container& cont) : span(cont.data(), narrow<index_type>(cont.size()))
+ {
+ }
+
+ template <class Container,
+ class = std::enable_if_t<
+ std::is_const<element_type>::value && !details::is_span<Container>::value &&
+ std::is_convertible<typename Container::pointer, pointer>::value &&
+ std::is_convertible<typename Container::pointer,
+ decltype(std::declval<Container>().data())>::value>>
+ constexpr span(const Container& cont) : span(cont.data(), narrow<index_type>(cont.size()))
+ {
+ }
+
+ constexpr span(const span& other) noexcept = default;
+
+ template <
+ class OtherElementType, std::ptrdiff_t OtherExtent,
+ class = std::enable_if_t<
+ details::is_allowed_extent_conversion<OtherExtent, Extent>::value &&
+ details::is_allowed_element_type_conversion<OtherElementType, element_type>::value>>
+ constexpr span(const span<OtherElementType, OtherExtent>& other)
+ : storage_(other.data(), details::extent_type<OtherExtent>(other.size()))
+ {
+ }
+
+ ~span() noexcept = default;
+ constexpr span& operator=(const span& other) noexcept = default;
+
+ // [span.sub], span subviews
+ template <std::ptrdiff_t Count>
+ constexpr span<element_type, Count> first() const
+ {
+ Expects(Count >= 0 && Count <= size());
+ return {data(), Count};
+ }
+
+ template <std::ptrdiff_t Count>
+ constexpr span<element_type, Count> last() const
+ {
+ Expects(Count >= 0 && size() - Count >= 0);
+ return {data() + (size() - Count), Count};
+ }
+
+ template <std::ptrdiff_t Offset, std::ptrdiff_t Count = dynamic_extent>
+ constexpr auto subspan() const -> typename details::calculate_subspan_type<ElementType, Extent, Offset, Count>::type
+ {
+ Expects((Offset >= 0 && size() - Offset >= 0) &&
+ (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size())));
+
+ return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
+ }
+
+ constexpr span<element_type, dynamic_extent> first(index_type count) const
+ {
+ Expects(count >= 0 && count <= size());
+ return {data(), count};
+ }
+
+ constexpr span<element_type, dynamic_extent> last(index_type count) const
+ {
+ return make_subspan(size() - count, dynamic_extent, subspan_selector<Extent>{});
+ }
+
+ constexpr span<element_type, dynamic_extent> subspan(index_type offset,
+ index_type count = dynamic_extent) const
+ {
+ return make_subspan(offset, count, subspan_selector<Extent>{});
+ }
+
+
+ // [span.obs], span observers
+ constexpr index_type size() const noexcept { return storage_.size(); }
+ constexpr index_type size_bytes() const noexcept
+ {
+ return size() * narrow_cast<index_type>(sizeof(element_type));
+ }
+ constexpr bool empty() const noexcept { return size() == 0; }
+
+ // [span.elem], span element access
+ constexpr reference operator[](index_type idx) const
+ {
+ Expects(idx >= 0 && idx < storage_.size());
+ return data()[idx];
+ }
+
+ constexpr reference at(index_type idx) const { return this->operator[](idx); }
+ constexpr reference operator()(index_type idx) const { return this->operator[](idx); }
+ constexpr pointer data() const noexcept { return storage_.data(); }
+
+ // [span.iter], span iterator support
+ constexpr iterator begin() const noexcept { return {this, 0}; }
+ constexpr iterator end() const noexcept { return {this, size()}; }
+
+ constexpr const_iterator cbegin() const noexcept { return {this, 0}; }
+ constexpr const_iterator cend() const noexcept { return {this, size()}; }
+
+ constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
+ constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
+
+ constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
+ constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
+
+#ifdef _MSC_VER
+ // Tell MSVC how to unwrap spans in range-based-for
+ constexpr pointer _Unchecked_begin() const noexcept { return data(); }
+ constexpr pointer _Unchecked_end() const noexcept { return data() + size(); }
+#endif // _MSC_VER
+
+private:
+
+ // Needed to remove unnecessary null check in subspans
+ struct KnownNotNull
+ {
+ pointer p;
+ };
+
+ // 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:
+ // KnownNotNull parameter is needed to remove unnecessary null check
+ // in subspans and constructors from arrays
+ template <class OtherExtentType>
+ constexpr storage_type(KnownNotNull data, OtherExtentType ext) : ExtentType(ext), data_(data.p)
+ {
+ Expects(ExtentType::size() >= 0);
+ }
+
+
+ template <class OtherExtentType>
+ constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data)
+ {
+ Expects(ExtentType::size() >= 0);
+ Expects(data || ExtentType::size() == 0);
+ }
+
+ constexpr pointer data() const noexcept { return data_; }
+
+ private:
+ pointer data_;
+ };
+
+ storage_type<details::extent_type<Extent>> storage_;
+
+ // The rest is needed to remove unnecessary null check
+ // in subspans and constructors from arrays
+ constexpr span(KnownNotNull ptr, index_type count) : storage_(ptr, count) {}
+
+ template <std::ptrdiff_t CallerExtent>
+ class subspan_selector {};
+
+ template <std::ptrdiff_t CallerExtent>
+ span<element_type, dynamic_extent> make_subspan(index_type offset,
+ index_type count,
+ subspan_selector<CallerExtent>) const
+ {
+ span<element_type, dynamic_extent> tmp(*this);
+ return tmp.subspan(offset, count);
+ }
+
+ span<element_type, dynamic_extent> make_subspan(index_type offset,
+ index_type count,
+ subspan_selector<dynamic_extent>) const
+ {
+ Expects(offset >= 0 && size() - offset >= 0);
+ if (count == dynamic_extent)
+ {
+ return { KnownNotNull{ data() + offset }, size() - offset };
+ }
+
+ Expects(count >= 0 && size() - offset >= count);
+ return { KnownNotNull{ data() + offset }, count };
+ }
+};
+
+#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND)
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr const typename span<ElementType, Extent>::index_type span<ElementType, Extent>::extent;
+#endif
+
+
+// [span.comparison], span comparison operators
+template <class ElementType, std::ptrdiff_t FirstExtent, std::ptrdiff_t SecondExtent>
+constexpr bool operator==(span<ElementType, FirstExtent> l,
+ span<ElementType, SecondExtent> r)
+{
+ return std::equal(l.begin(), l.end(), r.begin(), r.end());
+}
+
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr bool operator!=(span<ElementType, Extent> l,
+ span<ElementType, Extent> r)
+{
+ return !(l == r);
+}
+
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr bool operator<(span<ElementType, Extent> l,
+ span<ElementType, Extent> r)
+{
+ return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+}
+
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr bool operator<=(span<ElementType, Extent> l,
+ span<ElementType, Extent> r)
+{
+ return !(l > r);
+}
+
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr bool operator>(span<ElementType, Extent> l,
+ span<ElementType, Extent> r)
+{
+ return r < l;
+}
+
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr bool operator>=(span<ElementType, Extent> l,
+ span<ElementType, Extent> r)
+{
+ return !(l < r);
+}
+
+namespace 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 std::size_t, but older compilers may not see it as
+ // constexpr
+ // and so will fail compilation of the template
+ template <class ElementType, std::ptrdiff_t Extent>
+ struct calculate_byte_size
+ : std::integral_constant<std::ptrdiff_t,
+ static_cast<std::ptrdiff_t>(sizeof(ElementType) *
+ static_cast<std::size_t>(Extent))>
+ {
+ };
+
+ template <class ElementType>
+ struct calculate_byte_size<ElementType, dynamic_extent>
+ : std::integral_constant<std::ptrdiff_t, dynamic_extent>
+ {
+ };
+}
+
+// [span.objectrep], views of object representation
+template <class ElementType, std::ptrdiff_t Extent>
+span<const byte, details::calculate_byte_size<ElementType, Extent>::value>
+as_bytes(span<ElementType, Extent> s) noexcept
+{
+ return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
+}
+
+template <class ElementType, std::ptrdiff_t Extent,
+ class = std::enable_if_t<!std::is_const<ElementType>::value>>
+span<byte, details::calculate_byte_size<ElementType, Extent>::value>
+as_writeable_bytes(span<ElementType, Extent> s) noexcept
+{
+ return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
+}
+
+//
+// make_span() - Utility functions for creating spans
+//
+template <class ElementType>
+constexpr span<ElementType> make_span(ElementType* ptr, typename span<ElementType>::index_type count)
+{
+ return span<ElementType>(ptr, count);
+}
+
+template <class ElementType>
+constexpr span<ElementType> make_span(ElementType* firstElem, ElementType* lastElem)
+{
+ return span<ElementType>(firstElem, lastElem);
+}
+
+template <class ElementType, std::size_t N>
+constexpr span<ElementType, N> make_span(ElementType (&arr)[N]) noexcept
+{
+ return span<ElementType, N>(arr);
+}
+
+template <class Container>
+constexpr span<typename Container::value_type> make_span(Container& cont)
+{
+ return span<typename Container::value_type>(cont);
+}
+
+template <class Container>
+constexpr span<const typename Container::value_type> make_span(const Container& cont)
+{
+ return span<const typename Container::value_type>(cont);
+}
+
+template <class Ptr>
+constexpr span<typename Ptr::element_type> make_span(Ptr& cont, std::ptrdiff_t count)
+{
+ return span<typename Ptr::element_type>(cont, count);
+}
+
+template <class Ptr>
+constexpr span<typename Ptr::element_type> make_span(Ptr& cont)
+{
+ return span<typename Ptr::element_type>(cont);
+}
+
+// Specialization of gsl::at for span
+template <class ElementType, std::ptrdiff_t Extent>
+constexpr ElementType& at(span<ElementType, Extent> s, index i)
+{
+ // No bounds checking here because it is done in span::operator[] called below
+ return s[i];
+}
+
+} // namespace gsl
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // _MSC_VER < 1910
+
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#if __GNUC__ > 6
+#pragma GCC diagnostic pop
+#endif // __GNUC__ > 6
+
+#endif // GSL_SPAN_H
diff --git a/third_party/GSL/include/gsl/string_span b/third_party/GSL/include/gsl/string_span
new file mode 100644
index 0000000..c08f246
--- /dev/null
+++ b/third_party/GSL/include/gsl/string_span
@@ -0,0 +1,730 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GSL_STRING_SPAN_H
+#define GSL_STRING_SPAN_H
+
+#include <gsl/gsl_assert> // for Ensures, Expects
+#include <gsl/gsl_util> // for narrow_cast
+#include <gsl/span> // for operator!=, operator==, dynamic_extent
+
+#include <algorithm> // for equal, lexicographical_compare
+#include <array> // for array
+#include <cstddef> // for ptrdiff_t, size_t, nullptr_t
+#include <cstdint> // for PTRDIFF_MAX
+#include <cstring>
+#include <string> // for basic_string, allocator, char_traits
+#include <type_traits> // for declval, is_convertible, enable_if_t, add_...
+
+#ifdef _MSC_VER
+#pragma warning(push)
+
+// 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
+
+// In order to test the library, we need it to throw exceptions that we can catch
+#ifdef GSL_THROW_ON_CONTRACT_VIOLATION
+#define GSL_NOEXCEPT /*noexcept*/
+#else
+#define GSL_NOEXCEPT noexcept
+#endif // GSL_THROW_ON_CONTRACT_VIOLATION
+
+namespace gsl
+{
+//
+// czstring and wzstring
+//
+// These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays)
+// that allow static analysis to help find bugs.
+//
+// There are no additional features/semantics that we can find a way to add inside the
+// type system for these types that will not either incur significant runtime costs or
+// (sometimes needlessly) break existing programs when introduced.
+//
+
+template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
+using basic_zstring = CharT*;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using czstring = basic_zstring<const char, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cwzstring = basic_zstring<const wchar_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cu16zstring = basic_zstring<const char16_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cu32zstring = basic_zstring<const char32_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using zstring = basic_zstring<char, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using wzstring = basic_zstring<wchar_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using u16zstring = basic_zstring<char16_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using u32zstring = basic_zstring<char32_t, Extent>;
+
+namespace details
+{
+ template <class CharT>
+ std::ptrdiff_t string_length(const CharT* str, std::ptrdiff_t n)
+ {
+ if (str == nullptr || n <= 0) return 0;
+
+ const span<const CharT> str_span{str, n};
+
+ std::ptrdiff_t len = 0;
+ while (len < n && str_span[len]) len++;
+
+ return len;
+ }
+}
+
+//
+// ensure_sentinel()
+//
+// Provides a way to obtain an span from a contiguous sequence
+// that ends with a (non-inclusive) sentinel value.
+//
+// Will fail-fast if sentinel cannot be found before max elements are examined.
+//
+template <typename T, const T Sentinel>
+span<T, dynamic_extent> ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX)
+{
+ auto cur = seq;
+ while ((cur - seq) < max && *cur != Sentinel) ++cur;
+ Ensures(*cur == Sentinel);
+ return {seq, cur - seq};
+}
+
+//
+// ensure_z - creates a span for a zero terminated strings.
+// Will fail fast if a null-terminator cannot be found before
+// the limit of size_type.
+//
+template <typename CharT>
+span<CharT, dynamic_extent> ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRDIFF_MAX)
+{
+ return ensure_sentinel<CharT, CharT(0)>(sz, max);
+}
+
+template <typename CharT, std::size_t N>
+span<CharT, dynamic_extent> ensure_z(CharT (&sz)[N])
+{
+ return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N));
+}
+
+template <class Cont>
+span<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_extent>
+ensure_z(Cont& cont)
+{
+ return ensure_z(cont.data(), static_cast<std::ptrdiff_t>(cont.size()));
+}
+
+template <typename CharT, std::ptrdiff_t>
+class basic_string_span;
+
+namespace details
+{
+ template <typename T>
+ struct is_basic_string_span_oracle : std::false_type
+ {
+ };
+
+ template <typename CharT, std::ptrdiff_t Extent>
+ struct is_basic_string_span_oracle<basic_string_span<CharT, Extent>> : std::true_type
+ {
+ };
+
+ template <typename T>
+ struct is_basic_string_span : is_basic_string_span_oracle<std::remove_cv_t<T>>
+ {
+ };
+}
+
+//
+// string_span and relatives
+//
+template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
+class basic_string_span
+{
+public:
+ using element_type = CharT;
+ using pointer = std::add_pointer_t<element_type>;
+ using reference = std::add_lvalue_reference_t<element_type>;
+ using const_reference = std::add_lvalue_reference_t<std::add_const_t<element_type>>;
+ using impl_type = span<element_type, Extent>;
+
+ using index_type = typename impl_type::index_type;
+ using iterator = typename impl_type::iterator;
+ using const_iterator = typename impl_type::const_iterator;
+ using reverse_iterator = typename impl_type::reverse_iterator;
+ using const_reverse_iterator = typename impl_type::const_reverse_iterator;
+
+ // default (empty)
+ constexpr basic_string_span() GSL_NOEXCEPT = default;
+
+ // copy
+ constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default;
+
+ // assign
+ constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default;
+
+ constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {}
+ constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {}
+
+ // From static arrays - if 0-terminated, remove 0 from the view
+ // All other containers allow 0s within the length, so we do not remove them
+ template <std::size_t N>
+ constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr))
+ {
+ }
+
+ template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
+ constexpr basic_string_span(std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT : span_(arr)
+ {
+ }
+
+ template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
+ constexpr basic_string_span(const std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT
+ : span_(arr)
+ {
+ }
+
+ // Container signature should work for basic_string after C++17 version exists
+ template <class Traits, class Allocator>
+ constexpr basic_string_span(std::basic_string<element_type, Traits, Allocator>& str)
+ : span_(&str[0], narrow_cast<std::ptrdiff_t>(str.length()))
+ {
+ }
+
+ template <class Traits, class Allocator>
+ constexpr basic_string_span(const std::basic_string<element_type, Traits, Allocator>& str)
+ : span_(&str[0], str.length())
+ {
+ }
+
+ // from containers. Containers must have a pointer type and data() function signatures
+ template <class Container,
+ class = std::enable_if_t<
+ !details::is_basic_string_span<Container>::value &&
+ std::is_convertible<typename Container::pointer, pointer>::value &&
+ std::is_convertible<typename Container::pointer,
+ decltype(std::declval<Container>().data())>::value>>
+ constexpr basic_string_span(Container& cont) : span_(cont)
+ {
+ }
+
+ template <class Container,
+ class = std::enable_if_t<
+ !details::is_basic_string_span<Container>::value &&
+ std::is_convertible<typename Container::pointer, pointer>::value &&
+ std::is_convertible<typename Container::pointer,
+ decltype(std::declval<Container>().data())>::value>>
+ constexpr basic_string_span(const Container& cont) : span_(cont)
+ {
+ }
+
+ // from string_span
+ template <
+ class OtherValueType, std::ptrdiff_t OtherExtent,
+ class = std::enable_if_t<std::is_convertible<
+ typename basic_string_span<OtherValueType, OtherExtent>::impl_type, impl_type>::value>>
+ constexpr basic_string_span(basic_string_span<OtherValueType, OtherExtent> other)
+ : span_(other.data(), other.length())
+ {
+ }
+
+ template <index_type Count>
+ constexpr basic_string_span<element_type, Count> first() const
+ {
+ return {span_.template first<Count>()};
+ }
+
+ constexpr basic_string_span<element_type, dynamic_extent> first(index_type count) const
+ {
+ return {span_.first(count)};
+ }
+
+ template <index_type Count>
+ constexpr basic_string_span<element_type, Count> last() const
+ {
+ return {span_.template last<Count>()};
+ }
+
+ constexpr basic_string_span<element_type, dynamic_extent> last(index_type count) const
+ {
+ return {span_.last(count)};
+ }
+
+ template <index_type Offset, index_type Count>
+ constexpr basic_string_span<element_type, Count> subspan() const
+ {
+ return {span_.template subspan<Offset, Count>()};
+ }
+
+ constexpr basic_string_span<element_type, dynamic_extent>
+ subspan(index_type offset, index_type count = dynamic_extent) const
+ {
+ return {span_.subspan(offset, count)};
+ }
+
+ constexpr reference operator[](index_type idx) const { return span_[idx]; }
+ constexpr reference operator()(index_type idx) const { return span_[idx]; }
+
+ constexpr pointer data() const { return span_.data(); }
+
+ constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); }
+ constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); }
+ constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); }
+ constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); }
+ constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; }
+
+ constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); }
+ constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); }
+
+ constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); }
+ constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); }
+
+ constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); }
+ constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); }
+
+ constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); }
+ constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); }
+
+private:
+ static impl_type remove_z(pointer const& sz, std::ptrdiff_t max)
+ {
+ return {sz, details::string_length(sz, max)};
+ }
+
+ template <std::size_t N>
+ static impl_type remove_z(element_type (&sz)[N])
+ {
+ return remove_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
+ }
+
+ impl_type span_;
+};
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using string_span = basic_string_span<char, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cstring_span = basic_string_span<const char, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using wstring_span = basic_string_span<wchar_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cwstring_span = basic_string_span<const wchar_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using u16string_span = basic_string_span<char16_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cu16string_span = basic_string_span<const char16_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using u32string_span = basic_string_span<char32_t, Extent>;
+
+template <std::ptrdiff_t Extent = dynamic_extent>
+using cu32string_span = basic_string_span<const char32_t, Extent>;
+
+//
+// to_string() allow (explicit) conversions from string_span to string
+//
+
+template <typename CharT, std::ptrdiff_t Extent>
+std::basic_string<typename std::remove_const<CharT>::type>
+to_string(basic_string_span<CharT, Extent> view)
+{
+ return {view.data(), static_cast<std::size_t>(view.length())};
+}
+
+template <typename CharT, typename Traits = typename std::char_traits<CharT>,
+ typename Allocator = std::allocator<CharT>, typename gCharT, std::ptrdiff_t Extent>
+std::basic_string<CharT, Traits, Allocator> to_basic_string(basic_string_span<gCharT, Extent> view)
+{
+ return {view.data(), static_cast<std::size_t>(view.length())};
+}
+
+template <class ElementType, std::ptrdiff_t Extent>
+basic_string_span<const byte, details::calculate_byte_size<ElementType, Extent>::value>
+as_bytes(basic_string_span<ElementType, Extent> s) noexcept
+{
+ return { reinterpret_cast<const byte*>(s.data()), s.size_bytes() };
+}
+
+template <class ElementType, std::ptrdiff_t Extent,
+ class = std::enable_if_t<!std::is_const<ElementType>::value>>
+basic_string_span<byte, details::calculate_byte_size<ElementType, Extent>::value>
+as_writeable_bytes(basic_string_span<ElementType, Extent> s) noexcept
+{
+ return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
+}
+
+// zero-terminated string span, used to convert
+// zero-terminated spans to legacy strings
+template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
+class basic_zstring_span
+{
+public:
+ using value_type = CharT;
+ using const_value_type = std::add_const_t<CharT>;
+
+ using pointer = std::add_pointer_t<value_type>;
+ using const_pointer = std::add_pointer_t<const_value_type>;
+
+ using zstring_type = basic_zstring<value_type, Extent>;
+ using const_zstring_type = basic_zstring<const_value_type, Extent>;
+
+ using impl_type = span<value_type, Extent>;
+ using string_span_type = basic_string_span<value_type, Extent>;
+
+ constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s)
+ {
+ // expects a zero-terminated span
+ Expects(s[s.size() - 1] == '\0');
+ }
+
+ // copy
+ constexpr basic_zstring_span(const basic_zstring_span& other) = default;
+
+ // move
+ constexpr basic_zstring_span(basic_zstring_span&& other) = default;
+
+ // assign
+ constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default;
+
+ // move assign
+ constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default;
+
+ constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; }
+
+ constexpr string_span_type as_string_span() const GSL_NOEXCEPT
+ {
+ auto sz = span_.size();
+ return { span_.data(), sz > 1 ? sz - 1 : 0 };
+ }
+ constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); }
+
+ constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); }
+
+private:
+ impl_type span_;
+};
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using zstring_span = basic_zstring_span<char, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using wzstring_span = basic_zstring_span<wchar_t, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using u16zstring_span = basic_zstring_span<char16_t, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using u32zstring_span = basic_zstring_span<char32_t, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using czstring_span = basic_zstring_span<const char, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using cwzstring_span = basic_zstring_span<const wchar_t, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using cu16zstring_span = basic_zstring_span<const char16_t, Max>;
+
+template <std::ptrdiff_t Max = dynamic_extent>
+using cu32zstring_span = basic_zstring_span<const char32_t, Max>;
+
+// operator ==
+template <class CharT, std::ptrdiff_t Extent, class T,
+ class = std::enable_if_t<
+ details::is_basic_string_span<T>::value ||
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
+bool operator==(const gsl::basic_string_span<CharT, Extent>& one, const T& other) GSL_NOEXCEPT
+{
+ const gsl::basic_string_span<std::add_const_t<CharT>> tmp(other);
+ return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
+}
+
+template <class CharT, std::ptrdiff_t Extent, class T,
+ class = std::enable_if_t<
+ !details::is_basic_string_span<T>::value &&
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
+bool operator==(const T& one, const gsl::basic_string_span<CharT, Extent>& other) GSL_NOEXCEPT
+{
+ gsl::basic_string_span<std::add_const_t<CharT>> tmp(one);
+ return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
+}
+
+// operator !=
+template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<std::is_convertible<
+ T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
+bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return !(one == other);
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
+ !gsl::details::is_basic_string_span<T>::value>>
+bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return !(one == other);
+}
+
+// operator<
+template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<std::is_convertible<
+ T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
+bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ const gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
+ return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
+ !gsl::details::is_basic_string_span<T>::value>>
+bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
+ return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
+}
+
+#ifndef _MSC_VER
+
+// VS treats temp and const containers as convertible to basic_string_span,
+// so the cases below are already covered by the previous operators
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
+ return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
+ return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
+}
+#endif
+
+// operator <=
+template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<std::is_convertible<
+ T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
+bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return !(other < one);
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
+ !gsl::details::is_basic_string_span<T>::value>>
+bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return !(other < one);
+}
+
+#ifndef _MSC_VER
+
+// VS treats temp and const containers as convertible to basic_string_span,
+// so the cases below are already covered by the previous operators
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return !(other < one);
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return !(other < one);
+}
+#endif
+
+// operator>
+template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<std::is_convertible<
+ T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
+bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return other < one;
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
+ !gsl::details::is_basic_string_span<T>::value>>
+bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return other < one;
+}
+
+#ifndef _MSC_VER
+
+// VS treats temp and const containers as convertible to basic_string_span,
+// so the cases below are already covered by the previous operators
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return other < one;
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return other < one;
+}
+#endif
+
+// operator >=
+template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<std::is_convertible<
+ T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
+bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return !(one < other);
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename = std::enable_if_t<
+ std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
+ !gsl::details::is_basic_string_span<T>::value>>
+bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return !(one < other);
+}
+
+#ifndef _MSC_VER
+
+// VS treats temp and const containers as convertible to basic_string_span,
+// so the cases below are already covered by the previous operators
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
+{
+ return !(one < other);
+}
+
+template <
+ typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
+ typename DataType = typename T::value_type,
+ typename = std::enable_if_t<
+ !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
+ std::is_convertible<DataType*, CharT*>::value &&
+ std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
+ DataType>::value>>
+bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
+{
+ return !(one < other);
+}
+#endif
+} // namespace gsl
+
+#undef GSL_NOEXCEPT
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+
+#if _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // _MSC_VER < 1910
+#endif // _MSC_VER
+
+#endif // GSL_STRING_SPAN_H
diff --git a/third_party/GSL/tests/CMakeLists.txt b/third_party/GSL/tests/CMakeLists.txt
new file mode 100644
index 0000000..b5e9bb2
--- /dev/null
+++ b/third_party/GSL/tests/CMakeLists.txt
@@ -0,0 +1,170 @@
+cmake_minimum_required(VERSION 2.8.7)
+
+project(GSLTests CXX)
+
+# will make visual studio generated project group files
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+list(APPEND CATCH_CMAKE_ARGS
+ "-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external"
+ "-DNO_SELFTEST=true"
+)
+
+if(GIT_FOUND)
+ # add catch
+ ExternalProject_Add(
+ catch
+ PREFIX ${CMAKE_BINARY_DIR}/catch
+ GIT_REPOSITORY https://github.com/catchorg/Catch2.git
+ GIT_TAG v2.0.1
+ CMAKE_ARGS ${CATCH_CMAKE_ARGS}
+ LOG_DOWNLOAD 1
+ UPDATE_DISCONNECTED 1
+ )
+else()
+ # assume catch is installed in a system directory
+ add_custom_target(catch)
+endif()
+
+if (MSVC AND (GSL_CXX_STANDARD EQUAL 17))
+ set(GSL_CPLUSPLUS_OPT -Zc:__cplusplus)
+endif()
+
+# this interface adds compile options to how the tests are run
+# please try to keep entries ordered =)
+add_library(gsl_tests_config INTERFACE)
+target_compile_options(gsl_tests_config INTERFACE
+ $<$<CXX_COMPILER_ID:MSVC>:
+ /EHsc
+ /W4
+ /WX
+ >
+ ${GSL_CPLUSPLUS_OPT}
+ $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:
+ -fno-strict-aliasing
+ -Wall
+ -Wcast-align
+ -Wconversion
+ -Wctor-dtor-privacy
+ -Werror
+ -Wextra
+ -Wno-missing-braces
+ -Wnon-virtual-dtor
+ -Wold-style-cast
+ -Woverloaded-virtual
+ -Wpedantic
+ -Wshadow
+ -Wsign-conversion
+ >
+)
+
+# for tests to find the catch header
+target_include_directories(gsl_tests_config INTERFACE
+ ${CMAKE_BINARY_DIR}/external/include
+)
+
+# set definitions for tests
+target_compile_definitions(gsl_tests_config INTERFACE
+ GSL_THROW_ON_CONTRACT_VIOLATION
+)
+
+# create the main executable for each test. this reduces the compile time
+# of each test by pre-compiling catch.
+add_library(test_catch STATIC test.cpp)
+target_link_libraries(test_catch
+ GSL
+ gsl_tests_config
+)
+add_dependencies(test_catch catch)
+set_property(TARGET test_catch PROPERTY FOLDER "GSL_tests")
+
+function(add_gsl_test name)
+ add_executable(${name} ${name}.cpp)
+ target_link_libraries(${name}
+ GSL
+ test_catch
+ gsl_tests_config
+ )
+ add_dependencies(${name} catch)
+ add_test(
+ ${name}
+ ${name}
+ )
+ # group all tests under GSL_tests
+ set_property(TARGET ${name} PROPERTY FOLDER "GSL_tests")
+endfunction()
+
+add_gsl_test(span_tests)
+add_gsl_test(multi_span_tests)
+add_gsl_test(strided_span_tests)
+add_gsl_test(string_span_tests)
+add_gsl_test(at_tests)
+add_gsl_test(bounds_tests)
+add_gsl_test(notnull_tests)
+add_gsl_test(assertion_tests)
+add_gsl_test(utils_tests)
+add_gsl_test(owner_tests)
+add_gsl_test(byte_tests)
+add_gsl_test(algorithm_tests)
+
+
+# No exception tests
+
+foreach(flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ STRING (REGEX REPLACE "/EHsc" "" ${flag_var} "${${flag_var}}")
+endforeach(flag_var)
+
+# this interface adds compile options to how the tests are run
+# please try to keep entries ordered =)
+add_library(gsl_tests_config_noexcept INTERFACE)
+target_compile_options(gsl_tests_config_noexcept INTERFACE
+ $<$<CXX_COMPILER_ID:MSVC>:
+ /D_HAS_EXCEPTIONS=0
+ /wd4702
+ /wd4577
+ /W4
+ /WX
+ >
+ ${GSL_CPLUSPLUS_OPT}
+ $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:
+ -fno-strict-aliasing
+ -fno-exceptions
+ -Wall
+ -Wcast-align
+ -Wconversion
+ -Wctor-dtor-privacy
+ -Werror
+ -Wextra
+ -Wno-missing-braces
+ -Wnon-virtual-dtor
+ -Wold-style-cast
+ -Woverloaded-virtual
+ -Wpedantic
+ -Wshadow
+ -Wsign-conversion
+ >
+)
+
+# set definitions for tests
+target_compile_definitions(gsl_tests_config_noexcept INTERFACE
+ GSL_TERMINATE_ON_CONTRACT_VIOLATION
+)
+
+function(add_gsl_test_noexcept name)
+ add_executable(${name} ${name}.cpp)
+ target_link_libraries(${name}
+ GSL
+ gsl_tests_config_noexcept
+ )
+ add_test(
+ ${name}
+ ${name}
+ )
+ # group all tests under GSL_tests_noexcept
+ set_property(TARGET ${name} PROPERTY FOLDER "GSL_tests_noexcept")
+endfunction()
+
+add_gsl_test_noexcept(no_exception_throw_tests)
+add_gsl_test_noexcept(no_exception_ensure_tests)
diff --git a/third_party/GSL/tests/algorithm_tests.cpp b/third_party/GSL/tests/algorithm_tests.cpp
new file mode 100644
index 0000000..388d17d
--- /dev/null
+++ b/third_party/GSL/tests/algorithm_tests.cpp
@@ -0,0 +1,210 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, CHE...
+
+#include <gsl/gsl_algorithm> // for copy
+#include <gsl/span> // for span
+
+#include <array> // for array
+#include <cstddef> // for size_t
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using namespace std;
+using namespace gsl;
+
+TEST_CASE("same_type")
+{
+ // dynamic source and destination span
+ {
+ std::array<int, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<int> src_span(src);
+ span<int> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+
+ // static source and dynamic destination span
+ {
+ std::array<int, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<int, 5> src_span(src);
+ span<int> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+
+ // dynamic source and static destination span
+ {
+ std::array<int, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<int> src_span(src);
+ span<int, 10> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+
+ // static source and destination span
+ {
+ std::array<int, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<int, 5> src_span(src);
+ span<int, 10> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+}
+
+TEST_CASE("compatible_type")
+{
+ // dynamic source and destination span
+ {
+ std::array<short, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<short> src_span(src);
+ span<int> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+
+ // static source and dynamic destination span
+ {
+ std::array<short, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<short, 5> src_span(src);
+ span<int> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+
+ // dynamic source and static destination span
+ {
+ std::array<short, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<short> src_span(src);
+ span<int, 10> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+
+ // static source and destination span
+ {
+ std::array<short, 5> src{1, 2, 3, 4, 5};
+ std::array<int, 10> dst{};
+
+ span<short, 5> src_span(src);
+ span<int, 10> dst_span(dst);
+
+ copy(src_span, dst_span);
+ copy(src_span, dst_span.subspan(src_span.size()));
+
+ for (std::size_t i = 0; i < src.size(); ++i) {
+ CHECK(dst[i] == src[i]);
+ CHECK(dst[i + src.size()] == src[i]);
+ }
+ }
+}
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+TEST_CASE("incompatible_type")
+{
+ std::array<int, 4> src{1, 2, 3, 4};
+ std::array<int*, 12> dst{};
+
+ span<int> src_span_dyn(src);
+ span<int, 4> src_span_static(src);
+ span<int*> dst_span_dyn(dst);
+ span<int*, 4> dst_span_static(dst);
+
+ // every line should produce a compilation error
+ copy(src_span_dyn, dst_span_dyn);
+ copy(src_span_dyn, dst_span_static);
+ copy(src_span_static, dst_span_dyn);
+ copy(src_span_static, dst_span_static);
+}
+#endif
+
+TEST_CASE("small_destination_span")
+{
+ std::array<int, 12> src{1, 2, 3, 4};
+ std::array<int, 4> dst{};
+
+ span<int> src_span_dyn(src);
+ span<int, 12> src_span_static(src);
+ span<int> dst_span_dyn(dst);
+ span<int, 4> dst_span_static(dst);
+
+ CHECK_THROWS_AS(copy(src_span_dyn, dst_span_dyn), fail_fast);
+ CHECK_THROWS_AS(copy(src_span_dyn, dst_span_static), fail_fast);
+ CHECK_THROWS_AS(copy(src_span_static, dst_span_dyn), fail_fast);
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ copy(src_span_static, dst_span_static);
+#endif
+}
diff --git a/third_party/GSL/tests/assertion_tests.cpp b/third_party/GSL/tests/assertion_tests.cpp
new file mode 100644
index 0000000..25c0089
--- /dev/null
+++ b/third_party/GSL/tests/assertion_tests.cpp
@@ -0,0 +1,46 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, CHECK...
+
+#include <gsl/gsl_assert> // for fail_fast (ptr only), Ensures, Expects
+
+using namespace gsl;
+
+int f(int i)
+{
+ Expects(i > 0 && i < 10);
+ return i;
+}
+
+TEST_CASE("expects")
+{
+ CHECK(f(2) == 2);
+ CHECK_THROWS_AS(f(10), fail_fast);
+}
+
+int g(int i)
+{
+ i++;
+ Ensures(i > 0 && i < 10);
+ return i;
+}
+
+TEST_CASE("ensures")
+{
+ CHECK(g(2) == 3);
+ CHECK_THROWS_AS(g(9), fail_fast);
+}
diff --git a/third_party/GSL/tests/at_tests.cpp b/third_party/GSL/tests/at_tests.cpp
new file mode 100644
index 0000000..2f9e999
--- /dev/null
+++ b/third_party/GSL/tests/at_tests.cpp
@@ -0,0 +1,116 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK_THROW...
+
+#include <gsl/gsl_util> // for at
+
+#include <array> // for array
+#include <cstddef> // for size_t
+#include <initializer_list> // for initializer_list
+#include <vector> // for vector
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using gsl::fail_fast;
+
+TEST_CASE("static_array")
+{
+ int a[4] = {1, 2, 3, 4};
+ const int(&c_a)[4] = a;
+
+ for (int i = 0; i < 4; ++i) {
+ CHECK(&gsl::at(a, i) == &a[i]);
+ CHECK(&gsl::at(c_a, i) == &a[i]);
+ }
+
+ CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+ CHECK_THROWS_AS(gsl::at(c_a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(c_a, 4), fail_fast);
+}
+
+TEST_CASE("std_array")
+{
+ std::array<int, 4> a = {1, 2, 3, 4};
+ const std::array<int, 4>& c_a = a;
+
+ for (int i = 0; i < 4; ++i) {
+ CHECK(&gsl::at(a, i) == &a[static_cast<std::size_t>(i)]);
+ CHECK(&gsl::at(c_a, i) == &a[static_cast<std::size_t>(i)]);
+ }
+
+ CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+ CHECK_THROWS_AS(gsl::at(c_a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(c_a, 4), fail_fast);
+}
+
+TEST_CASE("StdVector")
+{
+ std::vector<int> a = {1, 2, 3, 4};
+ const std::vector<int>& c_a = a;
+
+ for (int i = 0; i < 4; ++i) {
+ CHECK(&gsl::at(a, i) == &a[static_cast<std::size_t>(i)]);
+ CHECK(&gsl::at(c_a, i) == &a[static_cast<std::size_t>(i)]);
+ }
+
+ CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+ CHECK_THROWS_AS(gsl::at(c_a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(c_a, 4), fail_fast);
+}
+
+TEST_CASE("InitializerList")
+{
+ std::initializer_list<int> a = {1, 2, 3, 4};
+
+ for (int i = 0; i < 4; ++i) {
+ CHECK(gsl::at(a, i) == i + 1);
+ CHECK(gsl::at({1, 2, 3, 4}, i) == i + 1);
+ }
+
+ CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+ CHECK_THROWS_AS(gsl::at({1, 2, 3, 4}, -1), fail_fast);
+ CHECK_THROWS_AS(gsl::at({1, 2, 3, 4}, 4), fail_fast);
+}
+
+#if !defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1910
+static constexpr bool test_constexpr()
+{
+ int a1[4] = {1, 2, 3, 4};
+ const int(&c_a1)[4] = a1;
+ std::array<int, 4> a2 = {1, 2, 3, 4};
+ const std::array<int, 4>& c_a2 = a2;
+
+ for (int i = 0; i < 4; ++i) {
+ if (&gsl::at(a1, i) != &a1[i]) return false;
+ if (&gsl::at(c_a1, i) != &a1[i]) return false;
+ // requires C++17:
+ // if (&gsl::at(a2, i) != &a2[static_cast<std::size_t>(i)]) return false;
+ if (&gsl::at(c_a2, i) != &c_a2[static_cast<std::size_t>(i)]) return false;
+ if (gsl::at({1, 2, 3, 4}, i) != i + 1) return false;
+ }
+
+ return true;
+}
+
+static_assert(test_constexpr(), "FAIL");
+#endif
diff --git a/third_party/GSL/tests/bounds_tests.cpp b/third_party/GSL/tests/bounds_tests.cpp
new file mode 100644
index 0000000..1f4b1e2
--- /dev/null
+++ b/third_party/GSL/tests/bounds_tests.cpp
@@ -0,0 +1,99 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, TEST_CASE
+
+#include <gsl/multi_span> // for static_bounds, static_bounds_dynamic_range_t
+
+#include <cstddef> // for ptrdiff_t, size_t
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using namespace std;
+using namespace gsl;
+
+namespace
+{
+void use(std::ptrdiff_t&) {}
+}
+
+TEST_CASE("basic_bounds")
+{
+ for (auto point : static_bounds<dynamic_range, 3, 4>{2}) {
+ for (decltype(point)::size_type j = 0;
+ j < static_cast<decltype(point)::size_type>(decltype(point)::rank); j++)
+ {
+ use(j);
+ use(point[static_cast<std::size_t>(j)]);
+ }
+ }
+}
+
+TEST_CASE("bounds_basic")
+{
+ static_bounds<3, 4, 5> b;
+ const auto a = b.slice();
+ (void) a;
+ static_bounds<4, dynamic_range, 2> x{4};
+ x.slice().slice();
+}
+
+TEST_CASE("arrayview_iterator")
+{
+ static_bounds<4, dynamic_range, 2> bounds{3};
+
+ const auto itr = bounds.begin();
+ (void) itr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 4, dynamic_range, 2> av(nullptr, bounds);
+
+ auto itr2 = av.cbegin();
+
+ for (auto& v : av) {
+ v = 4;
+ }
+ fill(av.begin(), av.end(), 0);
+#endif
+}
+
+TEST_CASE("bounds_convertible")
+{
+ static_bounds<7, 4, 2> b1;
+ static_bounds<7, dynamic_range, 2> b2 = b1;
+ (void) b2;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ static_bounds<7, dynamic_range, 1> b4 = b2;
+#endif
+
+ static_bounds<dynamic_range, dynamic_range, dynamic_range> b3 = b1;
+ static_bounds<7, 4, 2> b4 = b3;
+ (void) b4;
+
+ static_bounds<dynamic_range> b11;
+
+ static_bounds<dynamic_range> b5;
+ static_bounds<34> b6;
+
+ b5 = static_bounds<20>();
+ CHECK_THROWS_AS(b6 = b5, fail_fast);
+ b5 = static_bounds<34>();
+ b6 = b5;
+
+ CHECK(b5 == b6);
+ CHECK(b5.size() == b6.size());
+}
diff --git a/third_party/GSL/tests/byte_tests.cpp b/third_party/GSL/tests/byte_tests.cpp
new file mode 100644
index 0000000..41501ce
--- /dev/null
+++ b/third_party/GSL/tests/byte_tests.cpp
@@ -0,0 +1,124 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
+
+#include <gsl/gsl_byte> // for to_byte, to_integer, byte, operator&, ope...
+
+using namespace std;
+using namespace gsl;
+
+namespace
+{
+
+TEST_CASE("construction")
+{
+ {
+ const byte b = static_cast<byte>(4);
+ CHECK(static_cast<unsigned char>(b) == 4);
+ }
+
+ {
+ const byte b = byte(12);
+ CHECK(static_cast<unsigned char>(b) == 12);
+ }
+
+ {
+ const byte b = to_byte<12>();
+ CHECK(static_cast<unsigned char>(b) == 12);
+ }
+ {
+ const unsigned char uc = 12;
+ const byte b = to_byte(uc);
+ CHECK(static_cast<unsigned char>(b) == 12);
+ }
+
+ // waiting for C++17 enum class direct initializer support
+ //{
+ // byte b { 14 };
+ // CHECK(static_cast<unsigned char>(b) == 14);
+ //}
+}
+
+TEST_CASE("bitwise_operations")
+{
+ const byte b = to_byte<0xFF>();
+
+ byte a = to_byte<0x00>();
+ CHECK((b | a) == to_byte<0xFF>());
+ CHECK(a == to_byte<0x00>());
+
+ a |= b;
+ CHECK(a == to_byte<0xFF>());
+
+ a = to_byte<0x01>();
+ CHECK((b & a) == to_byte<0x01>());
+
+ a &= b;
+ CHECK(a == to_byte<0x01>());
+
+ CHECK((b ^ a) == to_byte<0xFE>());
+
+ CHECK(a == to_byte<0x01>());
+ a ^= b;
+ CHECK(a == to_byte<0xFE>());
+
+ a = to_byte<0x01>();
+ CHECK(~a == to_byte<0xFE>());
+
+ a = to_byte<0xFF>();
+ CHECK((a << 4) == to_byte<0xF0>());
+ CHECK((a >> 4) == to_byte<0x0F>());
+
+ a <<= 4;
+ CHECK(a == to_byte<0xF0>());
+ a >>= 4;
+ CHECK(a == to_byte<0x0F>());
+}
+
+TEST_CASE("to_integer")
+{
+ const byte b = to_byte<0x12>();
+
+ CHECK(0x12 == gsl::to_integer<char>(b));
+ CHECK(0x12 == gsl::to_integer<short>(b));
+ CHECK(0x12 == gsl::to_integer<long>(b));
+ CHECK(0x12 == gsl::to_integer<long long>(b));
+
+ CHECK(0x12 == gsl::to_integer<unsigned char>(b));
+ CHECK(0x12 == gsl::to_integer<unsigned short>(b));
+ CHECK(0x12 == gsl::to_integer<unsigned long>(b));
+ CHECK(0x12 == gsl::to_integer<unsigned long long>(b));
+
+ // CHECK(0x12 == gsl::to_integer<float>(b)); // expect compile-time error
+ // CHECK(0x12 == gsl::to_integer<double>(b)); // expect compile-time error
+}
+
+int modify_both(gsl::byte & b, int& i)
+{
+ i = 10;
+ b = to_byte<5>();
+ return i;
+}
+
+TEST_CASE("aliasing")
+{
+ int i{0};
+ const int res = modify_both(reinterpret_cast<byte&>(i), i);
+ CHECK(res == i);
+}
+
+}
diff --git a/third_party/GSL/tests/multi_span_tests.cpp b/third_party/GSL/tests/multi_span_tests.cpp
new file mode 100644
index 0000000..549dcbe
--- /dev/null
+++ b/third_party/GSL/tests/multi_span_tests.cpp
@@ -0,0 +1,1701 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, CHECK...
+
+#include <gsl/gsl_byte> // for byte
+#include <gsl/gsl_util> // for narrow_cast
+#include <gsl/multi_span> // for multi_span, contiguous_span_iterator, dim
+
+#include <algorithm> // for fill, for_each
+#include <array> // for array
+#include <iostream> // for ptrdiff_t, size_t
+#include <iterator> // for reverse_iterator, begin, end, operator!=
+#include <numeric> // for iota
+#include <stddef.h> // for ptrdiff_t
+#include <string> // for string
+#include <vector> // for vector
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using namespace std;
+using namespace gsl;
+
+namespace
+{
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
+}
+
+TEST_CASE("default_constructor")
+{
+ {
+ multi_span<int> s;
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int> cs;
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ multi_span<int, 0> s;
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int, 0> cs;
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 1> s;
+ CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+ }
+
+ {
+ multi_span<int> s{};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int> cs{};
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+}
+
+TEST_CASE("from_nullptr_constructor")
+{
+ {
+ multi_span<int> s = nullptr;
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int> cs = nullptr;
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ multi_span<int, 0> s = nullptr;
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int, 0> cs = nullptr;
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 1> s = nullptr;
+ CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+ }
+
+ {
+ multi_span<int> s{nullptr};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int> cs{nullptr};
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ multi_span<int*> s{nullptr};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int*> cs{nullptr};
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+}
+
+TEST_CASE("from_nullptr_length_constructor")
+{
+ {
+ multi_span<int> s{nullptr, 0};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int> cs{nullptr, 0};
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ multi_span<int, 0> s{nullptr, 0};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int, 0> cs{nullptr, 0};
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 1> s{nullptr, 0};
+ CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+ }
+
+ {
+ auto workaround_macro = []() { multi_span<int> s{nullptr, 1}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+ auto const_workaround_macro = []() { multi_span<const int> cs{nullptr, 1}; };
+ CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+ }
+
+ {
+ auto workaround_macro = []() { multi_span<int, 0> s{nullptr, 1}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+ auto const_workaround_macro = []() { multi_span<const int, 0> s{nullptr, 1}; };
+ CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+ }
+
+ {
+ multi_span<int*> s{nullptr, 0};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+
+ multi_span<const int*> cs{nullptr, 0};
+ CHECK((cs.length() == 0 && cs.data() == nullptr));
+ }
+}
+
+TEST_CASE("from_element_constructor")
+{
+ int i = 5;
+
+ {
+ multi_span<int> s = i;
+ CHECK((s.length() == 1 && s.data() == &i));
+ CHECK(s[0] == 5);
+
+ multi_span<const int> cs = i;
+ CHECK((cs.length() == 1 && cs.data() == &i));
+ CHECK(cs[0] == 5);
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const j = 1;
+ multi_span<int, 0> s = j;
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 0> s = i;
+ CHECK((s.length() == 0 && s.data() == &i));
+#endif
+ }
+
+ {
+ multi_span<int, 1> s = i;
+ CHECK((s.length() == 1 && s.data() == &i));
+ CHECK(s[0] == 5);
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 2> s = i;
+ CHECK((s.length() == 2 && s.data() == &i));
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_a_temp = []() -> int { return 4; };
+ auto use_a_span = [](multi_span<int> s) { (void) s; };
+ use_a_span(get_a_temp());
+#endif
+ }
+}
+
+TEST_CASE("from_pointer_length_constructor")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ multi_span<int> s{&arr[0], 2};
+ CHECK((s.length() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ multi_span<int, 2> s{&arr[0], 2};
+ CHECK((s.length() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ int* p = nullptr;
+ multi_span<int> s{p, 0};
+ CHECK((s.length() == 0 && s.data() == nullptr));
+ }
+
+ {
+ int* p = nullptr;
+ auto workaround_macro = [=]() { multi_span<int> s{p, 2}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+}
+
+TEST_CASE("from_pointer_pointer_constructor")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ multi_span<int> s{&arr[0], &arr[2]};
+ CHECK((s.length() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ multi_span<int, 2> s{&arr[0], &arr[2]};
+ CHECK((s.length() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ multi_span<int> s{&arr[0], &arr[0]};
+ CHECK((s.length() == 0 && s.data() == &arr[0]));
+ }
+
+ {
+ multi_span<int, 0> s{&arr[0], &arr[0]};
+ CHECK((s.length() == 0 && s.data() == &arr[0]));
+ }
+
+ {
+ auto workaround_macro = [&]() { multi_span<int> s{&arr[1], &arr[0]}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ int* p = nullptr;
+ auto workaround_macro = [&]() { multi_span<int> s{&arr[0], p}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ int* p = nullptr;
+ auto workaround_macro = [&]() { multi_span<int> s{p, p}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ int* p = nullptr;
+ auto workaround_macro = [&]() { multi_span<int> s{&arr[0], p}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+}
+
+TEST_CASE("from_array_constructor")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ multi_span<int> s{arr};
+ CHECK((s.length() == 5 && s.data() == &arr[0]));
+ }
+
+ {
+ multi_span<int, 5> s{arr};
+ CHECK((s.length() == 5 && s.data() == &arr[0]));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 6> s{arr};
+#endif
+ }
+
+ {
+ multi_span<int, 0> s{arr};
+ CHECK((s.length() == 0 && s.data() == &arr[0]));
+ }
+
+ int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
+
+ {
+ multi_span<int> s{arr2d};
+ CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+ CHECK((s[0] == 1 && s[5] == 6));
+ }
+
+ {
+ multi_span<int, 0> s{arr2d};
+ CHECK((s.length() == 0 && s.data() == &arr2d[0][0]));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 5> s{arr2d};
+#endif
+ }
+
+ {
+ multi_span<int, 6> s{arr2d};
+ CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+ CHECK((s[0] == 1 && s[5] == 6));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 7> s{arr2d};
+#endif
+ }
+
+ {
+ multi_span<int[3]> s{arr2d[0]};
+ CHECK((s.length() == 1 && s.data() == &arr2d[0]));
+ }
+
+ {
+ multi_span<int, 2, 3> s{arr2d};
+ CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+ auto workaround_macro = [&]() { return s[{1, 2}] == 6; };
+ CHECK(workaround_macro());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 3, 3> s{arr2d};
+#endif
+ }
+
+ int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+ {
+ multi_span<int> s{arr3d};
+ CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+ CHECK((s[0] == 1 && s[11] == 12));
+ }
+
+ {
+ multi_span<int, 0> s{arr3d};
+ CHECK((s.length() == 0 && s.data() == &arr3d[0][0][0]));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 11> s{arr3d};
+#endif
+ }
+
+ {
+ multi_span<int, 12> s{arr3d};
+ CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+ CHECK((s[0] == 1 && s[5] == 6));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 13> s{arr3d};
+#endif
+ }
+
+ {
+ multi_span<int[3][2]> s{arr3d[0]};
+ CHECK((s.length() == 1 && s.data() == &arr3d[0]));
+ }
+
+ {
+ multi_span<int, 3, 2, 2> s{arr3d};
+ CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+ auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; };
+ CHECK(workaround_macro());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 3, 3, 3> s{arr3d};
+#endif
+ }
+}
+
+TEST_CASE("from_dynamic_array_constructor")
+{
+ double(*arr)[3][4] = new double[100][3][4];
+
+ {
+ multi_span<double, dynamic_range, 3, 4> s(arr, 10);
+ CHECK((s.length() == 120 && s.data() == &arr[0][0][0]));
+ CHECK_THROWS_AS(s[10][3][4], fail_fast);
+ }
+
+ {
+ multi_span<double, dynamic_range, 4, 3> s(arr, 10);
+ CHECK((s.length() == 120 && s.data() == &arr[0][0][0]));
+ }
+
+ {
+ multi_span<double> s(arr, 10);
+ CHECK((s.length() == 120 && s.data() == &arr[0][0][0]));
+ }
+
+ {
+ multi_span<double, dynamic_range, 3, 4> s(arr, 0);
+ CHECK((s.length() == 0 && s.data() == &arr[0][0][0]));
+ }
+
+ delete[] arr;
+}
+
+TEST_CASE("from_std_array_constructor")
+{
+ std::array<int, 4> arr = {1, 2, 3, 4};
+
+ {
+ multi_span<int> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+ multi_span<const int> cs{arr};
+ CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+ }
+
+ {
+ multi_span<int, 4> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+ multi_span<const int, 4> cs{arr};
+ CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+ }
+
+ {
+ multi_span<int, 2> s{arr};
+ CHECK((s.size() == 2 && s.data() == arr.data()));
+
+ multi_span<const int, 2> cs{arr};
+ CHECK((cs.size() == 2 && cs.data() == arr.data()));
+ }
+
+ {
+ multi_span<int, 0> s{arr};
+ CHECK((s.size() == 0 && s.data() == arr.data()));
+
+ multi_span<const int, 0> cs{arr};
+ CHECK((cs.size() == 0 && cs.data() == arr.data()));
+ }
+
+ // TODO This is currently an unsupported scenario. We will come back to it as we revise
+ // the multidimensional interface and what transformations between dimensionality look like
+ //{
+ // multi_span<int, 2, 2> s{arr};
+ // CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
+ //}
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 5> s{arr};
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_an_array = []() { return std::array<int, 4>{1, 2, 3, 4}; };
+ auto take_a_span = [](multi_span<int> s) { (void) s; };
+ // try to take a temporary std::array
+ take_a_span(get_an_array());
+#endif
+ }
+}
+
+TEST_CASE("from_const_std_array_constructor")
+{
+ const std::array<int, 4> arr = {1, 2, 3, 4};
+
+ {
+ multi_span<const int> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+
+ {
+ multi_span<const int, 4> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+
+ {
+ multi_span<const int, 2> s{arr};
+ CHECK((s.size() == 2 && s.data() == arr.data()));
+ }
+
+ {
+ multi_span<const int, 0> s{arr};
+ CHECK((s.size() == 0 && s.data() == arr.data()));
+ }
+
+ // TODO This is currently an unsupported scenario. We will come back to it as we revise
+ // the multidimensional interface and what transformations between dimensionality look like
+ //{
+ // multi_span<int, 2, 2> s{arr};
+ // CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
+ //}
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<const int, 5> s{arr};
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_an_array = []() -> const std::array<int, 4> { return {1, 2, 3, 4}; };
+ auto take_a_span = [](multi_span<const int> s) { (void) s; };
+ // try to take a temporary std::array
+ take_a_span(get_an_array());
+#endif
+ }
+}
+
+TEST_CASE("from_container_constructor")
+{
+ std::vector<int> v = {1, 2, 3};
+ const std::vector<int> cv = v;
+
+ {
+ multi_span<int> s{v};
+ CHECK((s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data()));
+
+ multi_span<const int> cs{v};
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(v.size()) && cs.data() == v.data()));
+ }
+
+ std::string str = "hello";
+ const std::string cstr = "hello";
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<char> s{str};
+ CHECK((s.size() == narrow_cast<std::ptrdiff_t>(str.size()) && s.data() == str.data()));
+#endif
+ multi_span<const char> cs{str};
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(str.size()) && cs.data() == str.data()));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<char> s{cstr};
+#endif
+ multi_span<const char> cs{cstr};
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(cstr.size()) &&
+ cs.data() == cstr.data()));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_vector = []() -> std::vector<int> { return {}; };
+ auto use_span = [](multi_span<int> s) { (void) s; };
+ use_span(get_temp_vector());
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_string = []() -> std::string { return {}; };
+ auto use_span = [](multi_span<char> s) { (void) s; };
+ use_span(get_temp_string());
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_vector = []() -> const std::vector<int> { return {}; };
+ auto use_span = [](multi_span<const char> s) { (void) s; };
+ use_span(get_temp_vector());
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_string = []() -> const std::string { return {}; };
+ auto use_span = [](multi_span<const char> s) { (void) s; };
+ use_span(get_temp_string());
+#endif
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::map<int, int> m;
+ multi_span<int> s{m};
+#endif
+ }
+}
+
+TEST_CASE("from_convertible_span_constructor")
+{
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<int, 7, 4, 2> av1(nullptr, b1);
+
+ auto f = [&]() { multi_span<int, 7, 4, 2> av1(nullptr); };
+ CHECK_THROWS_AS(f(), fail_fast);
+#endif
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ static_bounds<std::size_t, 7, dynamic_range, 2> b12(b11);
+ b12 = b11;
+ b11 = b12;
+
+ multi_span<int, dynamic_range> av1 = nullptr;
+ multi_span<int, 7, dynamic_range, 2> av2(av1);
+ multi_span<int, 7, 4, 2> av2(av1);
+#endif
+
+ multi_span<DerivedClass> avd;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ multi_span<BaseClass> avb = avd;
+#endif
+ multi_span<const DerivedClass> avcd = avd;
+ (void) avcd;
+}
+
+TEST_CASE("copy_move_and_assignment")
+{
+ multi_span<int> s1;
+ CHECK(s1.empty());
+
+ int arr[] = {3, 4, 5};
+
+ multi_span<const int> s2 = arr;
+ CHECK((s2.length() == 3 && s2.data() == &arr[0]));
+
+ s2 = s1;
+ CHECK(s2.empty());
+
+ auto get_temp_span = [&]() -> multi_span<int> { return {&arr[1], 2}; };
+ auto use_span = [&](multi_span<const int> s) {
+ CHECK((s.length() == 2 && s.data() == &arr[1]));
+ };
+ use_span(get_temp_span());
+
+ s1 = get_temp_span();
+ CHECK((s1.length() == 2 && s1.data() == &arr[1]));
+}
+
+template <class Bounds>
+void fn(const Bounds&)
+{
+ static_assert(Bounds::static_size == 60, "static bounds is wrong size");
+}
+TEST_CASE("as_multi_span_reshape")
+{
+ int a[3][4][5];
+ auto av = as_multi_span(a);
+ fn(av.bounds());
+ auto av2 = as_multi_span(av, dim<60>());
+ auto av3 = as_multi_span(av2, dim<3>(), dim<4>(), dim<5>());
+ auto av4 = as_multi_span(av3, dim<4>(), dim(3), dim<5>());
+ auto av5 = as_multi_span(av4, dim<3>(), dim<4>(), dim<5>());
+ auto av6 = as_multi_span(av5, dim<12>(), dim(5));
+
+ fill(av6.begin(), av6.end(), 1);
+
+ auto av7 = as_bytes(av6);
+
+ auto av8 = as_multi_span<int>(av7);
+
+ CHECK(av8.size() == av6.size());
+ for (auto i = 0; i < av8.size(); i++) {
+ CHECK(av8[i] == 1);
+ }
+}
+
+TEST_CASE("first")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.first<2>().bounds() == static_bounds<2>()));
+ CHECK(av.first<2>().length() == 2);
+ CHECK(av.first(2).length() == 2);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.first<0>().bounds() == static_bounds<0>()));
+ CHECK(av.first<0>().length() == 0);
+ CHECK(av.first(0).length() == 0);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.first<5>().bounds() == static_bounds<5>()));
+ CHECK(av.first<5>().length() == 5);
+ CHECK(av.first(5).length() == 5);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(av.first<6>().bounds() == static_bounds<6>());
+ CHECK(av.first<6>().length() == 6);
+ CHECK(av.first<-1>().length() == -1);
+#endif
+ CHECK_THROWS_AS(av.first(6).length(), fail_fast);
+ }
+
+ {
+ multi_span<int, dynamic_range> av;
+ CHECK((av.first<0>().bounds() == static_bounds<0>()));
+ CHECK(av.first<0>().length() == 0);
+ CHECK(av.first(0).length() == 0);
+ }
+}
+
+TEST_CASE("last")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.last<2>().bounds() == static_bounds<2>()));
+ CHECK(av.last<2>().length() == 2);
+ CHECK(av.last(2).length() == 2);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.last<0>().bounds() == static_bounds<0>()));
+ CHECK(av.last<0>().length() == 0);
+ CHECK(av.last(0).length() == 0);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.last<5>().bounds() == static_bounds<5>()));
+ CHECK(av.last<5>().length() == 5);
+ CHECK(av.last(5).length() == 5);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK((av.last<6>().bounds() == static_bounds<6>()));
+ CHECK(av.last<6>().length() == 6);
+#endif
+ CHECK_THROWS_AS(av.last(6).length(), fail_fast);
+ }
+
+ {
+ multi_span<int, dynamic_range> av;
+ CHECK((av.last<0>().bounds() == static_bounds<0>()));
+ CHECK(av.last<0>().length() == 0);
+ CHECK(av.last(0).length() == 0);
+ }
+}
+
+TEST_CASE("subspan")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>()));
+ CHECK((av.subspan<2, 2>().length() == 2));
+ CHECK(av.subspan(2, 2).length() == 2);
+ CHECK(av.subspan(2, 3).length() == 3);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>()));
+ CHECK((av.subspan<0, 0>().length() == 0));
+ CHECK(av.subspan(0, 0).length() == 0);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>()));
+ CHECK((av.subspan<0, 5>().length() == 5));
+ CHECK(av.subspan(0, 5).length() == 5);
+ CHECK_THROWS_AS(av.subspan(0, 6).length(), fail_fast);
+ CHECK_THROWS_AS(av.subspan(1, 5).length(), fail_fast);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>()));
+ CHECK((av.subspan<5, 0>().length() == 0));
+ CHECK(av.subspan(5, 0).length() == 0);
+ CHECK_THROWS_AS(av.subspan(6, 0).length(), fail_fast);
+ }
+
+ {
+ multi_span<int, dynamic_range> av;
+ CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>()));
+ CHECK((av.subspan<0, 0>().length() == 0));
+ CHECK(av.subspan(0, 0).length() == 0);
+ CHECK_THROWS_AS((av.subspan<1, 0>().length()), fail_fast);
+ }
+
+ {
+ multi_span<int> av;
+ CHECK(av.subspan(0).length() == 0);
+ CHECK_THROWS_AS(av.subspan(1).length(), fail_fast);
+ }
+
+ {
+ multi_span<int> av = arr;
+ CHECK(av.subspan(0).length() == 5);
+ CHECK(av.subspan(1).length() == 4);
+ CHECK(av.subspan(4).length() == 1);
+ CHECK(av.subspan(5).length() == 0);
+ CHECK_THROWS_AS(av.subspan(6).length(), fail_fast);
+ auto av2 = av.subspan(1);
+ for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+ }
+
+ {
+ multi_span<int, 5> av = arr;
+ CHECK(av.subspan(0).length() == 5);
+ CHECK(av.subspan(1).length() == 4);
+ CHECK(av.subspan(4).length() == 1);
+ CHECK(av.subspan(5).length() == 0);
+ CHECK_THROWS_AS(av.subspan(6).length(), fail_fast);
+ auto av2 = av.subspan(1);
+ for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+ }
+}
+
+TEST_CASE("rank")
+{
+ int arr[2] = {1, 2};
+
+ {
+ multi_span<int> s;
+ CHECK(s.rank() == 1);
+ }
+
+ {
+ multi_span<int, 2> s = arr;
+ CHECK(s.rank() == 1);
+ }
+
+ int arr2d[1][1] = {};
+ {
+ multi_span<int, 1, 1> s = arr2d;
+ CHECK(s.rank() == 2);
+ }
+}
+
+TEST_CASE("extent")
+{
+ {
+ multi_span<int> s;
+ CHECK(s.extent() == 0);
+ CHECK(s.extent(0) == 0);
+ CHECK_THROWS_AS(s.extent(1), fail_fast);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(s.extent<1>() == 0);
+#endif
+ }
+
+ {
+ multi_span<int, 0> s;
+ CHECK(s.extent() == 0);
+ CHECK(s.extent(0) == 0);
+ CHECK_THROWS_AS(s.extent(1), fail_fast);
+ }
+
+ {
+ int arr2d[1][2] = {};
+
+ multi_span<int, 1, 2> s = arr2d;
+ CHECK(s.extent() == 1);
+ CHECK(s.extent<0>() == 1);
+ CHECK(s.extent<1>() == 2);
+ CHECK(s.extent(0) == 1);
+ CHECK(s.extent(1) == 2);
+ CHECK_THROWS_AS(s.extent(3), fail_fast);
+ }
+
+ {
+ int arr2d[1][2] = {};
+
+ multi_span<int, 0, 2> s = arr2d;
+ CHECK(s.extent() == 0);
+ CHECK(s.extent<0>() == 0);
+ CHECK(s.extent<1>() == 2);
+ CHECK(s.extent(0) == 0);
+ CHECK(s.extent(1) == 2);
+ CHECK_THROWS_AS(s.extent(3), fail_fast);
+ }
+}
+
+TEST_CASE("operator_function_call")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ multi_span<int> s = arr;
+ CHECK(s(0) == 1);
+ CHECK_THROWS_AS(s(5), fail_fast);
+ }
+
+ int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
+
+ {
+ multi_span<int, 2, 3> s = arr2d;
+ CHECK(s(0, 0) == 1);
+ CHECK(s(0, 1) == 2);
+ CHECK(s(1, 2) == 6);
+ }
+
+ int arr3d[2][2][2] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ {
+ multi_span<int, 2, 2, 2> s = arr3d;
+ CHECK(s(0, 0, 0) == 1);
+ CHECK(s(1, 1, 1) == 8);
+ }
+}
+
+TEST_CASE("comparison_operators")
+{
+ {
+ int arr[10][2];
+ auto s1 = as_multi_span(arr);
+ multi_span<const int, dynamic_range, 2> s2 = s1;
+
+ CHECK(s1 == s2);
+
+ multi_span<int, 20> s3 = as_multi_span(s1, dim(20));
+ CHECK((s3 == s2 && s3 == s1));
+ }
+
+ {
+ multi_span<int> s1 = nullptr;
+ multi_span<int> s2 = nullptr;
+ CHECK(s1 == s2);
+ CHECK(!(s1 != s2));
+ CHECK(!(s1 < s2));
+ CHECK(s1 <= s2);
+ CHECK(!(s1 > s2));
+ CHECK(s1 >= s2);
+ CHECK(s2 == s1);
+ CHECK(!(s2 != s1));
+ CHECK(!(s2 < s1));
+ CHECK(s2 <= s1);
+ CHECK(!(s2 > s1));
+ CHECK(s2 >= s1);
+ }
+
+ {
+ int arr[] = {2, 1}; // bigger
+
+ multi_span<int> s1 = nullptr;
+ multi_span<int> s2 = arr;
+
+ CHECK(s1 != s2);
+ CHECK(s2 != s1);
+ CHECK(!(s1 == s2));
+ CHECK(!(s2 == s1));
+ CHECK(s1 < s2);
+ CHECK(!(s2 < s1));
+ CHECK(s1 <= s2);
+ CHECK(!(s2 <= s1));
+ CHECK(s2 > s1);
+ CHECK(!(s1 > s2));
+ CHECK(s2 >= s1);
+ CHECK(!(s1 >= s2));
+ }
+
+ {
+ int arr1[] = {1, 2};
+ int arr2[] = {1, 2};
+ multi_span<int> s1 = arr1;
+ multi_span<int> s2 = arr2;
+
+ CHECK(s1 == s2);
+ CHECK(!(s1 != s2));
+ CHECK(!(s1 < s2));
+ CHECK(s1 <= s2);
+ CHECK(!(s1 > s2));
+ CHECK(s1 >= s2);
+ CHECK(s2 == s1);
+ CHECK(!(s2 != s1));
+ CHECK(!(s2 < s1));
+ CHECK(s2 <= s1);
+ CHECK(!(s2 > s1));
+ CHECK(s2 >= s1);
+ }
+
+ {
+ int arr[] = {1, 2, 3};
+
+ multi_span<int> s1 = {&arr[0], 2}; // shorter
+ multi_span<int> s2 = arr; // longer
+
+ CHECK(s1 != s2);
+ CHECK(s2 != s1);
+ CHECK(!(s1 == s2));
+ CHECK(!(s2 == s1));
+ CHECK(s1 < s2);
+ CHECK(!(s2 < s1));
+ CHECK(s1 <= s2);
+ CHECK(!(s2 <= s1));
+ CHECK(s2 > s1);
+ CHECK(!(s1 > s2));
+ CHECK(s2 >= s1);
+ CHECK(!(s1 >= s2));
+ }
+
+ {
+ int arr1[] = {1, 2}; // smaller
+ int arr2[] = {2, 1}; // bigger
+
+ multi_span<int> s1 = arr1;
+ multi_span<int> s2 = arr2;
+
+ CHECK(s1 != s2);
+ CHECK(s2 != s1);
+ CHECK(!(s1 == s2));
+ CHECK(!(s2 == s1));
+ CHECK(s1 < s2);
+ CHECK(!(s2 < s1));
+ CHECK(s1 <= s2);
+ CHECK(!(s2 <= s1));
+ CHECK(s2 > s1);
+ CHECK(!(s1 > s2));
+ CHECK(s2 >= s1);
+ CHECK(!(s1 >= s2));
+ }
+}
+
+TEST_CASE("basics")
+{
+ auto ptr = as_multi_span(new int[10], 10);
+ fill(ptr.begin(), ptr.end(), 99);
+ for (int num : ptr) {
+ CHECK(num == 99);
+ }
+
+ delete[] ptr.data();
+}
+
+TEST_CASE("bounds_checks")
+{
+ int arr[10][2];
+ auto av = as_multi_span(arr);
+
+ fill(begin(av), end(av), 0);
+
+ av[2][0] = 1;
+ av[1][1] = 3;
+
+ // out of bounds
+ CHECK_THROWS_AS(av[1][3] = 3, fail_fast);
+ CHECK_THROWS_AS((av[{1, 3}] = 3), fail_fast);
+
+ CHECK_THROWS_AS(av[10][2], fail_fast);
+ CHECK_THROWS_AS((av[{10, 2}]), fail_fast);
+
+ CHECK_THROWS_AS(av[-1][0], fail_fast);
+ CHECK_THROWS_AS((av[{-1, 0}]), fail_fast);
+
+ CHECK_THROWS_AS(av[0][-1], fail_fast);
+ CHECK_THROWS_AS((av[{0, -1}]), fail_fast);
+}
+
+void overloaded_func(multi_span<const int, dynamic_range, 3, 5> exp, int expected_value)
+{
+ for (auto val : exp) {
+ CHECK(val == expected_value);
+ }
+}
+
+void overloaded_func(multi_span<const char, dynamic_range, 3, 5> exp, char expected_value)
+{
+ for (auto val : exp) {
+ CHECK(val == expected_value);
+ }
+}
+
+void fixed_func(multi_span<int, 3, 3, 5> exp, int expected_value)
+{
+ for (auto val : exp) {
+ CHECK(val == expected_value);
+ }
+}
+
+TEST_CASE("span_parameter_test")
+{
+ auto data = new int[4][3][5];
+
+ auto av = as_multi_span(data, 4);
+
+ CHECK(av.size() == 60);
+
+ fill(av.begin(), av.end(), 34);
+
+ int count = 0;
+ for_each(av.rbegin(), av.rend(), [&](int val) { count += val; });
+ CHECK(count == 34 * 60);
+ overloaded_func(av, 34);
+
+ overloaded_func(as_multi_span(av, dim(4), dim(3), dim(5)), 34);
+
+ // fixed_func(av, 34);
+ delete[] data;
+}
+
+TEST_CASE("md_access")
+{
+ auto width = 5, height = 20;
+
+ auto imgSize = width * height;
+ auto image_ptr = new int[static_cast<std::size_t>(imgSize)][3];
+
+ // size check will be done
+ auto image_view =
+ as_multi_span(as_multi_span(image_ptr, imgSize), dim(height), dim(width), dim<3>());
+
+ iota(image_view.begin(), image_view.end(), 1);
+
+ int expected = 0;
+ for (auto i = 0; i < height; i++) {
+ for (auto j = 0; j < width; j++) {
+ CHECK(expected + 1 == image_view[i][j][0]);
+ CHECK(expected + 2 == image_view[i][j][1]);
+ CHECK(expected + 3 == image_view[i][j][2]);
+
+ auto val = image_view[{i, j, 0}];
+ CHECK(expected + 1 == val);
+ val = image_view[{i, j, 1}];
+ CHECK(expected + 2 == val);
+ val = image_view[{i, j, 2}];
+ CHECK(expected + 3 == val);
+
+ expected += 3;
+ }
+ }
+}
+
+TEST_CASE("as_multi_span")
+{
+ {
+ int* arr = new int[150];
+
+ auto av = as_multi_span(arr, dim<10>(), dim(3), dim<5>());
+
+ fill(av.begin(), av.end(), 24);
+ overloaded_func(av, 24);
+
+ delete[] arr;
+
+ array<int, 15> stdarr{0};
+ auto av2 = as_multi_span(stdarr);
+ overloaded_func(as_multi_span(av2, dim(1), dim<3>(), dim<5>()), 0);
+
+ string str = "ttttttttttttttt"; // size = 15
+ auto t = str.data();
+ (void) t;
+ auto av3 = as_multi_span(str);
+ overloaded_func(as_multi_span(av3, dim(1), dim<3>(), dim<5>()), 't');
+ }
+
+ {
+ string str;
+ multi_span<char> strspan = as_multi_span(str);
+ (void) strspan;
+ const string cstr;
+ multi_span<const char> cstrspan = as_multi_span(cstr);
+ (void) cstrspan;
+ }
+
+ {
+ int a[3][4][5];
+ auto av = as_multi_span(a);
+ const int(*b)[4][5];
+ b = a;
+ auto bv = as_multi_span(b, 3);
+
+ CHECK(av == bv);
+
+ const std::array<double, 3> arr = {0.0, 0.0, 0.0};
+ auto cv = as_multi_span(arr);
+ (void) cv;
+
+ vector<float> vec(3);
+ auto dv = as_multi_span(vec);
+ (void) dv;
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto dv2 = as_multi_span(std::move(vec));
+#endif
+ }
+}
+
+TEST_CASE("empty_spans")
+{
+ {
+ multi_span<int, 0> empty_av(nullptr);
+
+ CHECK(empty_av.bounds().index_bounds() == multi_span_index<1>{0});
+ CHECK_THROWS_AS(empty_av[0], fail_fast);
+ CHECK_THROWS_AS(empty_av.begin()[0], fail_fast);
+ CHECK_THROWS_AS(empty_av.cbegin()[0], fail_fast);
+ for (auto& v : empty_av) {
+ (void) v;
+ CHECK(false);
+ }
+ }
+
+ {
+ multi_span<int> empty_av = {};
+ CHECK(empty_av.bounds().index_bounds() == multi_span_index<1>{0});
+ CHECK_THROWS_AS(empty_av[0], fail_fast);
+ CHECK_THROWS_AS(empty_av.begin()[0], fail_fast);
+ CHECK_THROWS_AS(empty_av.cbegin()[0], fail_fast);
+ for (auto& v : empty_av) {
+ (void) v;
+ CHECK(false);
+ }
+ }
+}
+
+TEST_CASE("index_constructor")
+{
+ auto arr = new int[8];
+ for (int i = 0; i < 4; ++i) {
+ arr[2 * i] = 4 + i;
+ arr[2 * i + 1] = i;
+ }
+
+ multi_span<int, dynamic_range> av(arr, 8);
+
+ ptrdiff_t a[1] = {0};
+ multi_span_index<1> i = a;
+
+ CHECK(av[i] == 4);
+
+ auto av2 = as_multi_span(av, dim<4>(), dim(2));
+ ptrdiff_t a2[2] = {0, 1};
+ multi_span_index<2> i2 = a2;
+
+ CHECK(av2[i2] == 0);
+ CHECK(av2[0][i] == 4);
+
+ delete[] arr;
+}
+
+TEST_CASE("index_constructors")
+{
+ {
+ // components of the same type
+ multi_span_index<3> i1(0, 1, 2);
+ CHECK(i1[0] == 0);
+
+ // components of different types
+ std::size_t c0 = 0;
+ std::size_t c1 = 1;
+ multi_span_index<3> i2(c0, c1, 2);
+ CHECK(i2[0] == 0);
+
+ // from array
+ multi_span_index<3> i3 = {0, 1, 2};
+ CHECK(i3[0] == 0);
+
+ // from other index of the same size type
+ multi_span_index<3> i4 = i3;
+ CHECK(i4[0] == 0);
+
+ // default
+ multi_span_index<3> i7;
+ CHECK(i7[0] == 0);
+
+ // default
+ multi_span_index<3> i9 = {};
+ CHECK(i9[0] == 0);
+ }
+
+ {
+ // components of the same type
+ multi_span_index<1> i1(0);
+ CHECK(i1[0] == 0);
+
+ // components of different types
+ std::size_t c0 = 0;
+ multi_span_index<1> i2(c0);
+ CHECK(i2[0] == 0);
+
+ // from array
+ multi_span_index<1> i3 = {0};
+ CHECK(i3[0] == 0);
+
+ // from int
+ multi_span_index<1> i4 = 0;
+ CHECK(i4[0] == 0);
+
+ // from other index of the same size type
+ multi_span_index<1> i5 = i3;
+ CHECK(i5[0] == 0);
+
+ // default
+ multi_span_index<1> i8;
+ CHECK(i8[0] == 0);
+
+ // default
+ multi_span_index<1> i9 = {};
+ CHECK(i9[0] == 0);
+ }
+
+ #ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ multi_span_index<3> i1(0, 1);
+ multi_span_index<3> i2(0, 1, 2, 3);
+ multi_span_index<3> i3 = {0};
+ multi_span_index<3> i4 = {0, 1, 2, 3};
+ multi_span_index<1> i5 = {0, 1};
+ }
+ #endif
+}
+
+TEST_CASE("index_operations")
+{
+ ptrdiff_t a[3] = {0, 1, 2};
+ ptrdiff_t b[3] = {3, 4, 5};
+ multi_span_index<3> i = a;
+ multi_span_index<3> j = b;
+
+ CHECK(i[0] == 0);
+ CHECK(i[1] == 1);
+ CHECK(i[2] == 2);
+
+ {
+ multi_span_index<3> k = i + j;
+
+ CHECK(i[0] == 0);
+ CHECK(i[1] == 1);
+ CHECK(i[2] == 2);
+ CHECK(k[0] == 3);
+ CHECK(k[1] == 5);
+ CHECK(k[2] == 7);
+ }
+
+ {
+ multi_span_index<3> k = i * 3;
+
+ CHECK(i[0] == 0);
+ CHECK(i[1] == 1);
+ CHECK(i[2] == 2);
+ CHECK(k[0] == 0);
+ CHECK(k[1] == 3);
+ CHECK(k[2] == 6);
+ }
+
+ {
+ multi_span_index<3> k = 3 * i;
+
+ CHECK(i[0] == 0);
+ CHECK(i[1] == 1);
+ CHECK(i[2] == 2);
+ CHECK(k[0] == 0);
+ CHECK(k[1] == 3);
+ CHECK(k[2] == 6);
+ }
+
+ {
+ multi_span_index<2> k = details::shift_left(i);
+
+ CHECK(i[0] == 0);
+ CHECK(i[1] == 1);
+ CHECK(i[2] == 2);
+ CHECK(k[0] == 1);
+ CHECK(k[1] == 2);
+ }
+}
+
+void iterate_second_column(multi_span<int, dynamic_range, dynamic_range> av)
+{
+ auto length = av.size() / 2;
+
+ // view to the second column
+ auto section = av.section({0, 1}, {length, 1});
+
+ CHECK(section.size() == length);
+ for (auto i = 0; i < section.size(); ++i) {
+ CHECK(section[i][0] == av[i][1]);
+ }
+
+ for (auto i = 0; i < section.size(); ++i) {
+ auto idx = multi_span_index<2>{i, 0}; // avoid braces inside the CHECK macro
+ CHECK(section[idx] == av[i][1]);
+ }
+
+ CHECK(section.bounds().index_bounds()[0] == length);
+ CHECK(section.bounds().index_bounds()[1] == 1);
+ for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) {
+ for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) {
+ auto idx = multi_span_index<2>{i, j}; // avoid braces inside the CHECK macro
+ CHECK(section[idx] == av[i][1]);
+ }
+ }
+
+ auto check_sum = 0;
+ for (auto i = 0; i < length; ++i) {
+ check_sum += av[i][1];
+ }
+
+ {
+ auto idx = 0;
+ auto sum = 0;
+ for (auto num : section) {
+ CHECK(num == av[idx][1]);
+ sum += num;
+ idx++;
+ }
+
+ CHECK(sum == check_sum);
+ }
+ {
+ auto idx = length - 1;
+ auto sum = 0;
+ for (auto iter = section.rbegin(); iter != section.rend(); ++iter) {
+ CHECK(*iter == av[idx][1]);
+ sum += *iter;
+ idx--;
+ }
+
+ CHECK(sum == check_sum);
+ }
+}
+
+TEST_CASE("span_section_iteration")
+{
+ int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}};
+
+ // static bounds
+ {
+ multi_span<int, 4, 2> av = arr;
+ iterate_second_column(av);
+ }
+ // first bound is dynamic
+ {
+ multi_span<int, dynamic_range, 2> av = arr;
+ iterate_second_column(av);
+ }
+ // second bound is dynamic
+ {
+ multi_span<int, 4, dynamic_range> av = arr;
+ iterate_second_column(av);
+ }
+ // both bounds are dynamic
+ {
+ multi_span<int, dynamic_range, dynamic_range> av = arr;
+ iterate_second_column(av);
+ }
+}
+
+TEST_CASE("dynamic_span_section_iteration")
+{
+ auto height = 4, width = 2;
+ auto size = height * width;
+
+ auto arr = new int[static_cast<std::size_t>(size)];
+ for (auto i = 0; i < size; ++i) {
+ arr[i] = i;
+ }
+
+ auto av = as_multi_span(arr, size);
+
+ // first bound is dynamic
+ {
+ multi_span<int, dynamic_range, 2> av2 = as_multi_span(av, dim(height), dim(width));
+ iterate_second_column(av2);
+ }
+ // second bound is dynamic
+ {
+ multi_span<int, 4, dynamic_range> av2 = as_multi_span(av, dim(height), dim(width));
+ iterate_second_column(av2);
+ }
+ // both bounds are dynamic
+ {
+ multi_span<int, dynamic_range, dynamic_range> av2 =
+ as_multi_span(av, dim(height), dim(width));
+ iterate_second_column(av2);
+ }
+
+ delete[] arr;
+}
+
+TEST_CASE("span_structure_size")
+{
+ double(*arr)[3][4] = new double[100][3][4];
+ multi_span<double, dynamic_range, 3, 4> av1(arr, 10);
+
+ struct EffectiveStructure
+ {
+ double* v1;
+ ptrdiff_t v2;
+ };
+ CHECK(sizeof(av1) == sizeof(EffectiveStructure));
+
+ CHECK_THROWS_AS(av1[10][3][4], fail_fast);
+
+ multi_span<const double, dynamic_range, 6, 4> av2 =
+ as_multi_span(av1, dim(5), dim<6>(), dim<4>());
+ (void) av2;
+}
+
+TEST_CASE("fixed_size_conversions")
+{
+ int arr[] = {1, 2, 3, 4};
+
+ // converting to an multi_span from an equal size array is ok
+ multi_span<int, 4> av4 = arr;
+ CHECK(av4.length() == 4);
+
+ // converting to dynamic_range a_v is always ok
+ {
+ multi_span<int, dynamic_range> av = av4;
+ (void) av;
+ }
+ {
+ multi_span<int, dynamic_range> av = arr;
+ (void) av;
+ }
+
+// initialization or assignment to static multi_span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ multi_span<int, 2> av2 = arr;
+ }
+ {
+ multi_span<int, 2> av2 = av4;
+ }
+#endif
+
+ {
+ multi_span<int, dynamic_range> av = arr;
+ multi_span<int, 2> av2 = av;
+ (void) av2;
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ multi_span<int, dynamic_range> av = arr;
+ multi_span<int, 2, 1> av2 = av.as_multi_span(dim<2>(), dim<2>());
+ }
+#endif
+
+ {
+ multi_span<int, dynamic_range> av = arr;
+ multi_span<int, 2, 1> av2 = as_multi_span(av, dim(2), dim(2));
+ auto workaround_macro = [&]() { return av2[{1, 0}] == 2; };
+ CHECK(workaround_macro());
+ }
+
+ // but doing so explicitly is ok
+
+ // you can convert statically
+ {
+ multi_span<int, 2> av2 = {arr, 2};
+ (void) av2;
+ }
+ {
+ multi_span<int, 1> av2 = av4.first<1>();
+ (void) av2;
+ }
+
+ // ...or dynamically
+ {
+ // NB: implicit conversion to multi_span<int,2> from multi_span<int,dynamic_range>
+ multi_span<int, 1> av2 = av4.first(1);
+ (void) av2;
+ }
+
+ // initialization or assignment to static multi_span that requires size INCREASE is not ok.
+ int arr2[2] = {1, 2};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ multi_span<int, 4> av4 = arr2;
+ }
+ {
+ multi_span<int, 2> av2 = arr2;
+ multi_span<int, 4> av4 = av2;
+ }
+#endif
+ {
+ auto f = [&]() {
+ multi_span<int, 4> av9 = {arr2, 2};
+ (void) av9;
+ };
+ CHECK_THROWS_AS(f(), fail_fast);
+ }
+
+ // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one
+ multi_span<int, dynamic_range> av = arr2;
+ auto f = [&]() {
+ multi_span<int, 4> av2 = av;
+ (void) av2;
+ };
+ CHECK_THROWS_AS(f(), fail_fast);
+}
+
+TEST_CASE("as_writeable_bytes")
+{
+ int a[] = {1, 2, 3, 4};
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ // you should not be able to get writeable bytes for const objects
+ multi_span<const int, dynamic_range> av = a;
+ auto wav = av.as_writeable_bytes();
+#endif
+ }
+
+ {
+ multi_span<int, dynamic_range> av;
+ auto wav = as_writeable_bytes(av);
+ CHECK(wav.length() == av.length());
+ CHECK(wav.length() == 0);
+ CHECK(wav.size_bytes() == 0);
+ }
+
+ {
+ multi_span<int, dynamic_range> av = a;
+ auto wav = as_writeable_bytes(av);
+ CHECK(wav.data() == reinterpret_cast<byte*>(&a[0]));
+ CHECK(static_cast<std::size_t>(wav.length()) == sizeof(a));
+ }
+}
+
+TEST_CASE("iterator")
+{
+ int a[] = {1, 2, 3, 4};
+
+ {
+ multi_span<int, dynamic_range> av = a;
+ auto wav = as_writeable_bytes(av);
+ for (auto& b : wav) {
+ b = byte(0);
+ }
+ for (std::size_t i = 0; i < 4; ++i) {
+ CHECK(a[i] == 0);
+ }
+ }
+
+ {
+ multi_span<int, dynamic_range> av = a;
+ for (auto& n : av) {
+ n = 1;
+ }
+ for (std::size_t i = 0; i < 4; ++i) {
+ CHECK(a[i] == 1);
+ }
+ }
+}
diff --git a/third_party/GSL/tests/no_exception_ensure_tests.cpp b/third_party/GSL/tests/no_exception_ensure_tests.cpp
new file mode 100644
index 0000000..5da021e
--- /dev/null
+++ b/third_party/GSL/tests/no_exception_ensure_tests.cpp
@@ -0,0 +1,53 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <cstdlib> // for std::exit
+#include <gsl/span> // for span
+
+int operator_subscript_no_throw()
+{
+ int arr[10];
+ gsl::span<int> sp { arr };
+ return sp[11];
+}
+
+
+void test_terminate()
+{
+ std::exit(0);
+}
+
+void setup_termination_handler()
+{
+#if defined(_MSC_VER)
+
+ auto& handler = gsl::details::get_terminate_handler();
+ handler = &test_terminate;
+
+#else
+
+ std::set_terminate(test_terminate);
+
+#endif
+}
+
+
+int main()
+{
+ setup_termination_handler();
+ operator_subscript_no_throw();
+ return -1;
+}
diff --git a/third_party/GSL/tests/no_exception_throw_tests.cpp b/third_party/GSL/tests/no_exception_throw_tests.cpp
new file mode 100644
index 0000000..dd4e994
--- /dev/null
+++ b/third_party/GSL/tests/no_exception_throw_tests.cpp
@@ -0,0 +1,51 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <cstdlib> // for std::exit
+#include <gsl/gsl_util> // for narrow
+
+int narrow_no_throw()
+{
+ long long bigNumber = 0x0fffffffffffffff;
+ return gsl::narrow<int>(bigNumber);
+}
+
+void test_terminate()
+{
+ std::exit(0);
+}
+
+void setup_termination_handler()
+{
+#if defined(_MSC_VER)
+
+ auto& handler = gsl::details::get_terminate_handler();
+ handler = &test_terminate;
+
+#else
+
+ std::set_terminate(test_terminate);
+
+#endif
+}
+
+
+int main()
+{
+ setup_termination_handler();
+ narrow_no_throw();
+ return -1;
+}
diff --git a/third_party/GSL/tests/notnull_tests.cpp b/third_party/GSL/tests/notnull_tests.cpp
new file mode 100644
index 0000000..1cb9c10
--- /dev/null
+++ b/third_party/GSL/tests/notnull_tests.cpp
@@ -0,0 +1,390 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
+
+#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
+
+#include <algorithm> // for addressof
+#include <memory> // for shared_ptr, make_shared, operator<, opera...
+#include <sstream> // for operator<<, ostringstream, basic_ostream:...
+#include <stdint.h> // for uint16_t
+#include <string> // for basic_string, operator==, string, operator<<
+#include <typeinfo> // for type_info
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using namespace gsl;
+
+struct MyBase
+{
+};
+struct MyDerived : public MyBase
+{
+};
+struct Unrelated
+{
+};
+
+// stand-in for a user-defined ref-counted class
+template <typename T>
+struct RefCounted
+{
+ RefCounted(T* p) : p_(p) {}
+ operator T*() { return p_; }
+ T* p_;
+};
+
+// user defined smart pointer with comparison operators returning non bool value
+template <typename T>
+struct CustomPtr
+{
+ CustomPtr(T* p) : p_(p) {}
+ operator T*() { return p_; }
+ bool operator!=(std::nullptr_t) const { return p_ != nullptr; }
+ T* p_ = nullptr;
+};
+
+template <typename T, typename U>
+std::string operator==(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+ return reinterpret_cast<const void*>(lhs.p_) == reinterpret_cast<const void*>(rhs.p_) ? "true"
+ : "false";
+}
+
+template <typename T, typename U>
+std::string operator!=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+ return reinterpret_cast<const void*>(lhs.p_) != reinterpret_cast<const void*>(rhs.p_) ? "true"
+ : "false";
+}
+
+template <typename T, typename U>
+std::string operator<(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+ return reinterpret_cast<const void*>(lhs.p_) < reinterpret_cast<const void*>(rhs.p_) ? "true"
+ : "false";
+}
+
+template <typename T, typename U>
+std::string operator>(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+ return reinterpret_cast<const void*>(lhs.p_) > reinterpret_cast<const void*>(rhs.p_) ? "true"
+ : "false";
+}
+
+template <typename T, typename U>
+std::string operator<=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+ return reinterpret_cast<const void*>(lhs.p_) <= reinterpret_cast<const void*>(rhs.p_) ? "true"
+ : "false";
+}
+
+template <typename T, typename U>
+std::string operator>=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+ return reinterpret_cast<const void*>(lhs.p_) >= reinterpret_cast<const void*>(rhs.p_) ? "true"
+ : "false";
+}
+
+struct NonCopyableNonMovable
+{
+ NonCopyableNonMovable() = default;
+ NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
+ NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
+ NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
+ NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
+};
+
+bool helper(not_null<int*> p) { return *p == 12; }
+bool helper_const(not_null<const int*> p) { return *p == 12; }
+
+TEST_CASE("TestNotNullConstructors")
+{
+#ifdef CONFIRM_COMPILATION_ERRORS
+ not_null<int*> p = nullptr; // yay...does not compile!
+ not_null<std::vector<char>*> p = 0; // yay...does not compile!
+ not_null<int*> p; // yay...does not compile!
+ std::unique_ptr<int> up = std::make_unique<int>(120);
+ not_null<int*> p = up;
+
+ // Forbid non-nullptr assignable types
+ not_null<std::vector<int>> f(std::vector<int>{1});
+ not_null<int> z(10);
+ not_null<std::vector<int>> y({1, 2});
+#endif
+ int i = 12;
+ auto rp = RefCounted<int>(&i);
+ not_null<int*> p(rp);
+ CHECK(p.get() == &i);
+
+ not_null<std::shared_ptr<int>> x(
+ std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
+
+#ifdef GSL_THROW_ON_CONTRACT_VIOLATION
+ int* pi = nullptr;
+ CHECK_THROWS_AS(not_null<decltype(pi)>(pi), fail_fast);
+#endif
+}
+
+template<typename T>
+void ostream_helper(T v)
+{
+ not_null<T*> p(&v);
+ {
+ std::ostringstream os;
+ std::ostringstream ref;
+ os << p;
+ ref << &v;
+ CHECK(os.str() == ref.str());
+ }
+ {
+ std::ostringstream os;
+ std::ostringstream ref;
+ os << *p;
+ ref << v;
+ CHECK(os.str() == ref.str());
+ }
+}
+
+TEST_CASE("TestNotNullostream")
+{
+ ostream_helper<int>(17);
+ ostream_helper<float>(21.5f);
+ ostream_helper<double>(3.4566e-7f);
+ ostream_helper<char>('c');
+ ostream_helper<uint16_t>(0x0123u);
+ ostream_helper<const char*>("cstring");
+ ostream_helper<std::string>("string");
+}
+
+
+TEST_CASE("TestNotNullCasting")
+{
+ MyBase base;
+ MyDerived derived;
+ Unrelated unrelated;
+ not_null<Unrelated*> u{&unrelated};
+ (void) u;
+ not_null<MyDerived*> p{&derived};
+ not_null<MyBase*> q(&base);
+ q = p; // allowed with heterogeneous copy ctor
+ CHECK(q == p);
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ q = u; // no viable conversion possible between MyBase* and Unrelated*
+ p = q; // not possible to implicitly convert MyBase* to MyDerived*
+
+ not_null<Unrelated*> r = p;
+ not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
+#endif
+ not_null<Unrelated*> t(reinterpret_cast<Unrelated*>(p.get()));
+ CHECK(reinterpret_cast<void*>(p.get()) == reinterpret_cast<void*>(t.get()));
+}
+
+TEST_CASE("TestNotNullAssignment")
+{
+ int i = 12;
+ not_null<int*> p(&i);
+ CHECK(helper(p));
+
+ int* q = nullptr;
+ CHECK_THROWS_AS(p = not_null<int*>(q), fail_fast);
+}
+
+TEST_CASE("TestNotNullRawPointerComparison")
+{
+ int ints[2] = {42, 43};
+ int* p1 = &ints[0];
+ const int* p2 = &ints[1];
+
+ using NotNull1 = not_null<decltype(p1)>;
+ using NotNull2 = not_null<decltype(p2)>;
+
+ CHECK((NotNull1(p1) == NotNull1(p1)) == true);
+ CHECK((NotNull1(p1) == NotNull2(p2)) == false);
+
+ CHECK((NotNull1(p1) != NotNull1(p1)) == false);
+ CHECK((NotNull1(p1) != NotNull2(p2)) == true);
+
+ CHECK((NotNull1(p1) < NotNull1(p1)) == false);
+ CHECK((NotNull1(p1) < NotNull2(p2)) == (p1 < p2));
+ CHECK((NotNull2(p2) < NotNull1(p1)) == (p2 < p1));
+
+ CHECK((NotNull1(p1) > NotNull1(p1)) == false);
+ CHECK((NotNull1(p1) > NotNull2(p2)) == (p1 > p2));
+ CHECK((NotNull2(p2) > NotNull1(p1)) == (p2 > p1));
+
+ CHECK((NotNull1(p1) <= NotNull1(p1)) == true);
+ CHECK((NotNull1(p1) <= NotNull2(p2)) == (p1 <= p2));
+ CHECK((NotNull2(p2) <= NotNull1(p1)) == (p2 <= p1));
+
+}
+
+TEST_CASE("TestNotNullDereferenceOperator")
+{
+ {
+ auto sp1 = std::make_shared<NonCopyableNonMovable>();
+
+ using NotNullSp1 = not_null<decltype(sp1)>;
+ CHECK(typeid(*sp1) == typeid(*NotNullSp1(sp1)));
+ CHECK(std::addressof(*NotNullSp1(sp1)) == std::addressof(*sp1));
+ }
+
+ {
+ int ints[1] = { 42 };
+ CustomPtr<int> p1(&ints[0]);
+
+ using NotNull1 = not_null<decltype(p1)>;
+ CHECK(typeid(*NotNull1(p1)) == typeid(*p1));
+ CHECK(*NotNull1(p1) == 42);
+ *NotNull1(p1) = 43;
+ CHECK(ints[0] == 43);
+ }
+
+ {
+ int v = 42;
+ gsl::not_null<int*> p(&v);
+ CHECK(typeid(*p) == typeid(*(&v)));
+ *p = 43;
+ CHECK(v == 43);
+ }
+}
+
+TEST_CASE("TestNotNullSharedPtrComparison")
+{
+ auto sp1 = std::make_shared<int>(42);
+ auto sp2 = std::make_shared<const int>(43);
+
+ using NotNullSp1 = not_null<decltype(sp1)>;
+ using NotNullSp2 = not_null<decltype(sp2)>;
+
+ CHECK((NotNullSp1(sp1) == NotNullSp1(sp1)) == true);
+ CHECK((NotNullSp1(sp1) == NotNullSp2(sp2)) == false);
+
+ CHECK((NotNullSp1(sp1) != NotNullSp1(sp1)) == false);
+ CHECK((NotNullSp1(sp1) != NotNullSp2(sp2)) == true);
+
+ CHECK((NotNullSp1(sp1) < NotNullSp1(sp1)) == false);
+ CHECK((NotNullSp1(sp1) < NotNullSp2(sp2)) == (sp1 < sp2));
+ CHECK((NotNullSp2(sp2) < NotNullSp1(sp1)) == (sp2 < sp1));
+
+ CHECK((NotNullSp1(sp1) > NotNullSp1(sp1)) == false);
+ CHECK((NotNullSp1(sp1) > NotNullSp2(sp2)) == (sp1 > sp2));
+ CHECK((NotNullSp2(sp2) > NotNullSp1(sp1)) == (sp2 > sp1));
+
+ CHECK((NotNullSp1(sp1) <= NotNullSp1(sp1)) == true);
+ CHECK((NotNullSp1(sp1) <= NotNullSp2(sp2)) == (sp1 <= sp2));
+ CHECK((NotNullSp2(sp2) <= NotNullSp1(sp1)) == (sp2 <= sp1));
+
+ CHECK((NotNullSp1(sp1) >= NotNullSp1(sp1)) == true);
+ CHECK((NotNullSp1(sp1) >= NotNullSp2(sp2)) == (sp1 >= sp2));
+ CHECK((NotNullSp2(sp2) >= NotNullSp1(sp1)) == (sp2 >= sp1));
+}
+
+TEST_CASE("TestNotNullCustomPtrComparison")
+{
+ int ints[2] = {42, 43};
+ CustomPtr<int> p1(&ints[0]);
+ CustomPtr<const int> p2(&ints[1]);
+
+ using NotNull1 = not_null<decltype(p1)>;
+ using NotNull2 = not_null<decltype(p2)>;
+
+ CHECK((NotNull1(p1) == NotNull1(p1)) == "true");
+ CHECK((NotNull1(p1) == NotNull2(p2)) == "false");
+
+ CHECK((NotNull1(p1) != NotNull1(p1)) == "false");
+ CHECK((NotNull1(p1) != NotNull2(p2)) == "true");
+
+ CHECK((NotNull1(p1) < NotNull1(p1)) == "false");
+ CHECK((NotNull1(p1) < NotNull2(p2)) == (p1 < p2));
+ CHECK((NotNull2(p2) < NotNull1(p1)) == (p2 < p1));
+
+ CHECK((NotNull1(p1) > NotNull1(p1)) == "false");
+ CHECK((NotNull1(p1) > NotNull2(p2)) == (p1 > p2));
+ CHECK((NotNull2(p2) > NotNull1(p1)) == (p2 > p1));
+
+ CHECK((NotNull1(p1) <= NotNull1(p1)) == "true");
+ CHECK((NotNull1(p1) <= NotNull2(p2)) == (p1 <= p2));
+ CHECK((NotNull2(p2) <= NotNull1(p1)) == (p2 <= p1));
+
+ CHECK((NotNull1(p1) >= NotNull1(p1)) == "true");
+ CHECK((NotNull1(p1) >= NotNull2(p2)) == (p1 >= p2));
+ CHECK((NotNull2(p2) >= NotNull1(p1)) == (p2 >= p1));
+}
+
+
+#if defined(__cplusplus) && (__cplusplus >= 201703L)
+TEST_CASE("TestNotNullConstructorTypeDeduction")
+{
+ {
+ int i = 42;
+
+ not_null x{&i};
+ helper(not_null{&i});
+ helper_const(not_null{&i});
+
+ CHECK(*x == 42);
+ }
+
+ {
+ int i = 42;
+ int* p = &i;
+
+ not_null x{p};
+ helper(not_null{p});
+ helper_const(not_null{p});
+
+ CHECK(*x == 42);
+ }
+
+ {
+ auto workaround_macro = []() {
+ int* p1 = nullptr;
+ not_null x{p1};
+ };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ auto workaround_macro = []() {
+ const int* p1 = nullptr;
+ not_null x{p1};
+ };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ int* p = nullptr;
+
+ CHECK_THROWS_AS(helper(not_null{p}), fail_fast);
+ CHECK_THROWS_AS(helper_const(not_null{p}), fail_fast);
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ not_null x{nullptr};
+ helper(not_null{nullptr});
+ helper_const(not_null{nullptr});
+ }
+#endif
+}
+#endif // #if defined(__cplusplus) && (__cplusplus >= 201703L)
+
+static_assert(std::is_nothrow_move_constructible<not_null<void *>>::value, "not_null must be no-throw move constructible");
diff --git a/third_party/GSL/tests/owner_tests.cpp b/third_party/GSL/tests/owner_tests.cpp
new file mode 100644
index 0000000..94822f5
--- /dev/null
+++ b/third_party/GSL/tests/owner_tests.cpp
@@ -0,0 +1,42 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
+
+#include <gsl/pointers> // for owner
+
+using namespace gsl;
+
+void f(int* i) { *i += 1; }
+
+TEST_CASE("basic_test")
+{
+ owner<int*> p = new int(120);
+ CHECK(*p == 120);
+ f(p);
+ CHECK(*p == 121);
+ delete p;
+}
+
+TEST_CASE("check_pointer_constraint")
+{
+ #ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ owner<int> integerTest = 10;
+ owner<std::shared_ptr<int>> sharedPtrTest(new int(10));
+ }
+ #endif
+}
diff --git a/third_party/GSL/tests/span_tests.cpp b/third_party/GSL/tests/span_tests.cpp
new file mode 100644
index 0000000..20279ec
--- /dev/null
+++ b/third_party/GSL/tests/span_tests.cpp
@@ -0,0 +1,1464 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
+
+#include <gsl/gsl_byte> // for byte
+#include <gsl/gsl_util> // for narrow_cast, at
+#include <gsl/span> // for span, span_iterator, operator==, operator!=
+
+#include <array> // for array
+#include <iostream> // for ptrdiff_t
+#include <iterator> // for reverse_iterator, operator-, operator==
+#include <memory> // for unique_ptr, shared_ptr, make_unique, allo...
+#include <regex> // for match_results, sub_match, match_results<>...
+#include <stddef.h> // for ptrdiff_t
+#include <string> // for string
+#include <type_traits> // for integral_constant<>::value, is_default_co...
+#include <vector> // for vector
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using namespace std;
+using namespace gsl;
+
+namespace
+{
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
+}
+
+TEST_CASE("default_constructor")
+{
+ {
+ span<int> s;
+ CHECK((s.size() == 0 && s.data() == nullptr));
+
+ span<const int> cs;
+ CHECK((cs.size() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ span<int, 0> s;
+ CHECK((s.size() == 0 && s.data() == nullptr));
+
+ span<const int, 0> cs;
+ CHECK((cs.size() == 0 && cs.data() == nullptr));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ span<int, 1> s;
+ CHECK((s.size() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+ }
+
+ {
+ span<int> s{};
+ CHECK((s.size() == 0 && s.data() == nullptr));
+
+ span<const int> cs{};
+ CHECK((cs.size() == 0 && cs.data() == nullptr));
+ }
+}
+
+TEST_CASE("size_optimization")
+{
+ {
+ span<int> s;
+ CHECK(sizeof(s) == sizeof(int*) + sizeof(ptrdiff_t));
+ }
+
+ {
+ span<int, 0> s;
+ CHECK(sizeof(s) == sizeof(int*));
+ }
+}
+
+TEST_CASE("from_nullptr_size_constructor")
+{
+ {
+ span<int> s{nullptr, static_cast<span<int>::index_type>(0)};
+ CHECK((s.size() == 0 && s.data() == nullptr));
+
+ span<const int> cs{nullptr, static_cast<span<int>::index_type>(0)};
+ CHECK((cs.size() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ span<int, 0> s{nullptr, static_cast<span<int>::index_type>(0)};
+ CHECK((s.size() == 0 && s.data() == nullptr));
+
+ span<const int, 0> cs{nullptr, static_cast<span<int>::index_type>(0)};
+ CHECK((cs.size() == 0 && cs.data() == nullptr));
+ }
+
+ {
+ auto workaround_macro = []() {
+ span<int, 1> s{nullptr, static_cast<span<int>::index_type>(0)};
+ };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ auto workaround_macro = []() { span<int> s{nullptr, 1}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+ auto const_workaround_macro = []() { span<const int> cs{nullptr, 1}; };
+ CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+ }
+
+ {
+ auto workaround_macro = []() { span<int, 0> s{nullptr, 1}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+ auto const_workaround_macro = []() { span<const int, 0> s{nullptr, 1}; };
+ CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+ }
+
+ {
+ span<int*> s{nullptr, static_cast<span<int>::index_type>(0)};
+ CHECK((s.size() == 0 && s.data() == nullptr));
+
+ span<const int*> cs{nullptr, static_cast<span<int>::index_type>(0)};
+ CHECK((cs.size() == 0 && cs.data() == nullptr));
+ }
+}
+
+TEST_CASE("from_pointer_length_constructor")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ for(int i = 0; i<4 ; ++i)
+ {
+ {
+ span<int> s = { &arr[0], i };
+ CHECK(s.size() == i);
+ CHECK(s.data() == &arr[0]);
+ CHECK(s.empty() == (i == 0));
+ for (int j = 0; j < i; ++j)
+ {
+ CHECK(arr[j] == s[j]);
+ CHECK(arr[j] == s.at(j));
+ CHECK(arr[j] == s(j));
+ }
+ }
+ {
+ span<int> s = { &arr[i], 4-i };
+ CHECK(s.size() == 4-i);
+ CHECK(s.data() == &arr[i]);
+ CHECK(s.empty() == (4-i == 0));
+ for (int j = 0; j < 4-i; ++j)
+ {
+ CHECK(arr[j+i] == s[j]);
+ CHECK(arr[j+i] == s.at(j));
+ CHECK(arr[j+i] == s(j));
+ }
+ }
+ }
+ }
+
+ {
+ span<int, 2> s{&arr[0], 2};
+ CHECK((s.size() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ int* p = nullptr;
+ span<int> s{p, static_cast<span<int>::index_type>(0)};
+ CHECK((s.size() == 0 && s.data() == nullptr));
+ }
+
+ {
+ int* p = nullptr;
+ auto workaround_macro = [=]() { span<int> s{p, 2}; };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ {
+ auto s = make_span(&arr[0], 2);
+ CHECK((s.size() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ int* p = nullptr;
+ auto s = make_span(p, static_cast<span<int>::index_type>(0));
+ CHECK((s.size() == 0 && s.data() == nullptr));
+ }
+
+ {
+ int* p = nullptr;
+ auto workaround_macro = [=]() { make_span(p, 2); };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+}
+
+TEST_CASE("from_pointer_pointer_constructor")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ span<int> s{&arr[0], &arr[2]};
+ CHECK((s.size() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ span<int, 2> s{&arr[0], &arr[2]};
+ CHECK((s.size() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ span<int> s{&arr[0], &arr[0]};
+ CHECK((s.size() == 0 && s.data() == &arr[0]));
+ }
+
+ {
+ span<int, 0> s{&arr[0], &arr[0]};
+ CHECK((s.size() == 0 && 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_THROWS_AS(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_THROWS_AS(workaround_macro(), fail_fast);
+ //}
+
+ {
+ int* p = nullptr;
+ span<int> s{p, p};
+ CHECK((s.size() == 0 && s.data() == nullptr));
+ }
+
+ {
+ int* p = nullptr;
+ span<int, 0> s{p, p};
+ CHECK((s.size() == 0 && 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_THROWS_AS(workaround_macro(), fail_fast);
+ //}
+
+ {
+ auto s = make_span(&arr[0], &arr[2]);
+ CHECK((s.size() == 2 && s.data() == &arr[0]));
+ CHECK((s[0] == 1 && s[1] == 2));
+ }
+
+ {
+ auto s = make_span(&arr[0], &arr[0]);
+ CHECK((s.size() == 0 && s.data() == &arr[0]));
+ }
+
+ {
+ int* p = nullptr;
+ auto s = make_span(p, p);
+ CHECK((s.size() == 0 && s.data() == nullptr));
+ }
+}
+
+TEST_CASE("from_array_constructor")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ span<int> s{arr};
+ CHECK((s.size() == 5 && s.data() == &arr[0]));
+ }
+
+ {
+ span<int, 5> s{arr};
+ CHECK((s.size() == 5 && 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};
+ CHECK((s.size() == 0 && s.data() == &arr[0]));
+ }
+
+ {
+ span<int> s{arr2d};
+ CHECK((s.size() == 6 && s.data() == &arr2d[0][0]));
+ CHECK((s[0] == 1 && s[5] == 6));
+ }
+
+ {
+ span<int, 0> s{arr2d};
+ CHECK((s.size() == 0 && s.data() == &arr2d[0][0]));
+ }
+
+ {
+ span<int, 6> s{arr2d};
+ }
+#endif
+ {
+ span<int[3]> s{&(arr2d[0]), 1};
+ CHECK((s.size() == 1 && 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};
+ CHECK((s.size() == 12 && s.data() == &arr3d[0][0][0]));
+ CHECK((s[0] == 1 && s[11] == 12));
+ }
+
+ {
+ span<int, 0> s{arr3d};
+ CHECK((s.size() == 0 && s.data() == &arr3d[0][0][0]));
+ }
+
+ {
+ span<int, 11> s{arr3d};
+ }
+
+ {
+ span<int, 12> s{arr3d};
+ CHECK((s.size() == 12 && s.data() == &arr3d[0][0][0]));
+ CHECK((s[0] == 1 && s[5] == 6));
+ }
+#endif
+ {
+ span<int[3][2]> s{&arr3d[0], 1};
+ CHECK((s.size() == 1 && s.data() == &arr3d[0]));
+ }
+
+ {
+ auto s = make_span(arr);
+ CHECK((s.size() == 5 && s.data() == &arr[0]));
+ }
+
+ {
+ auto s = make_span(&(arr2d[0]), 1);
+ CHECK((s.size() == 1 && s.data() == &arr2d[0]));
+ }
+
+ {
+ auto s = make_span(&arr3d[0], 1);
+ CHECK((s.size() == 1 && s.data() == &arr3d[0]));
+ }
+}
+
+TEST_CASE("from_dynamic_array_constructor")
+{
+ double(*arr)[3][4] = new double[100][3][4];
+
+ {
+ span<double> s(&arr[0][0][0], 10);
+ CHECK((s.size() == 10 && s.data() == &arr[0][0][0]));
+ }
+
+ {
+ auto s = make_span(&arr[0][0][0], 10);
+ CHECK((s.size() == 10 && s.data() == &arr[0][0][0]));
+ }
+
+ delete[] arr;
+}
+
+TEST_CASE("from_std_array_constructor")
+{
+ std::array<int, 4> arr = {1, 2, 3, 4};
+
+ {
+ span<int> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+ span<const int> cs{arr};
+ CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+ }
+
+ {
+ span<int, 4> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+ span<const int, 4> cs{arr};
+ CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ span<int, 2> s{arr};
+ CHECK((s.size() == 2 && s.data() == arr.data()));
+
+ span<const int, 2> cs{arr};
+ CHECK((cs.size() == 2 && cs.data() == arr.data()));
+ }
+
+ {
+ span<int, 0> s{arr};
+ CHECK((s.size() == 0 && s.data() == arr.data()));
+
+ span<const int, 0> cs{arr};
+ CHECK((cs.size() == 0 && 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 = make_span(arr);
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+}
+
+TEST_CASE("from_const_std_array_constructor")
+{
+ const std::array<int, 4> arr = {1, 2, 3, 4};
+
+ {
+ span<const int> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+
+ {
+ span<const int, 4> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ span<const int, 2> s{arr};
+ CHECK((s.size() == 2 && s.data() == arr.data()));
+ }
+
+ {
+ span<const int, 0> s{arr};
+ CHECK((s.size() == 0 && 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 = make_span(arr);
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+}
+
+TEST_CASE("from_std_array_const_constructor")
+{
+ std::array<const int, 4> arr = {1, 2, 3, 4};
+
+ {
+ span<const int> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+
+ {
+ span<const int, 4> s{arr};
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ span<const int, 2> s{arr};
+ CHECK((s.size() == 2 && s.data() == arr.data()));
+ }
+
+ {
+ span<const int, 0> s{arr};
+ CHECK((s.size() == 0 && s.data() == arr.data()));
+ }
+
+ {
+ span<const int, 5> s{arr};
+ }
+
+ {
+ span<int, 4> s{arr};
+ }
+#endif
+
+ {
+ auto s = make_span(arr);
+ CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+ }
+}
+
+TEST_CASE("from_container_constructor")
+{
+ std::vector<int> v = {1, 2, 3};
+ const std::vector<int> cv = v;
+
+ {
+ span<int> s{v};
+ CHECK((s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data()));
+
+ span<const int> cs{v};
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(v.size()) && cs.data() == v.data()));
+ }
+
+ std::string str = "hello";
+ const std::string cstr = "hello";
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ span<char> s{str};
+ CHECK((s.size() == narrow_cast<std::ptrdiff_t>(str.size()) && s.data() == str.data()));
+#endif
+ span<const char> cs{str};
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(str.size()) && cs.data() == str.data()));
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ span<char> s{cstr};
+#endif
+ span<const char> cs{cstr};
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(cstr.size()) &&
+ 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 = make_span(v);
+ CHECK((s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data()));
+
+ auto cs = make_span(cv);
+ CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(cv.size()) && cs.data() == cv.data()));
+ }
+}
+
+TEST_CASE("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
+}
+
+TEST_CASE("copy_move_and_assignment")
+{
+ span<int> s1;
+ CHECK(s1.empty());
+
+ int arr[] = {3, 4, 5};
+
+ span<const int> s2 = arr;
+ CHECK((s2.size() == 3 && s2.data() == &arr[0]));
+
+ s2 = s1;
+ CHECK(s2.empty());
+
+ auto get_temp_span = [&]() -> span<int> { return {&arr[1], 2}; };
+ auto use_span = [&](span<const int> s) { CHECK((s.size() == 2 && s.data() == &arr[1])); };
+ use_span(get_temp_span());
+
+ s1 = get_temp_span();
+ CHECK((s1.size() == 2 && s1.data() == &arr[1]));
+}
+
+TEST_CASE("first")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.first<2>().size() == 2);
+ CHECK(av.first(2).size() == 2);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.first<0>().size() == 0);
+ CHECK(av.first(0).size() == 0);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.first<5>().size() == 5);
+ CHECK(av.first(5).size() == 5);
+ }
+
+ {
+ span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(av.first<6>().size() == 6);
+ CHECK(av.first<-1>().size() == -1);
+#endif
+ CHECK_THROWS_AS(av.first(6).size(), fail_fast);
+ }
+
+ {
+ span<int> av;
+ CHECK(av.first<0>().size() == 0);
+ CHECK(av.first(0).size() == 0);
+ }
+}
+
+TEST_CASE("last")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.last<2>().size() == 2);
+ CHECK(av.last(2).size() == 2);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.last<0>().size() == 0);
+ CHECK(av.last(0).size() == 0);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.last<5>().size() == 5);
+ CHECK(av.last(5).size() == 5);
+ }
+
+ {
+ span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(av.last<6>().size() == 6);
+#endif
+ CHECK_THROWS_AS(av.last(6).size(), fail_fast);
+ }
+
+ {
+ span<int> av;
+ CHECK(av.last<0>().size() == 0);
+ CHECK(av.last(0).size() == 0);
+ }
+}
+
+TEST_CASE("subspan")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ span<int, 5> av = arr;
+ CHECK((av.subspan<2, 2>().size() == 2));
+ CHECK(decltype(av.subspan<2, 2>())::extent == 2);
+ CHECK(av.subspan(2, 2).size() == 2);
+ CHECK(av.subspan(2, 3).size() == 3);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK((av.subspan<0, 0>().size() == 0));
+ CHECK(decltype(av.subspan<0,0>())::extent == 0);
+ CHECK(av.subspan(0, 0).size() == 0);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK((av.subspan<0, 5>().size() == 5));
+ CHECK(decltype(av.subspan<0, 5>())::extent == 5);
+ CHECK(av.subspan(0, 5).size() == 5);
+
+ CHECK_THROWS_AS(av.subspan(0, 6).size(), fail_fast);
+ CHECK_THROWS_AS(av.subspan(1, 5).size(), fail_fast);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK((av.subspan<4, 0>().size() == 0));
+ CHECK(decltype(av.subspan<4, 0>())::extent == 0);
+ CHECK(av.subspan(4, 0).size() == 0);
+ CHECK(av.subspan(5, 0).size() == 0);
+ CHECK_THROWS_AS(av.subspan(6, 0).size(), fail_fast);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK((av.subspan<1>().size() == 4));
+ CHECK(decltype(av.subspan<1>())::extent == 4);
+ }
+
+ {
+ span<int> av;
+ CHECK((av.subspan<0, 0>().size() == 0));
+ CHECK((decltype(av.subspan<0, 0>())::extent == 0));
+ CHECK(av.subspan(0, 0).size() == 0);
+ CHECK_THROWS_AS((av.subspan<1, 0>().size()), fail_fast);
+ }
+
+ {
+ span<int> av;
+ CHECK(av.subspan(0).size() == 0);
+ CHECK_THROWS_AS(av.subspan(1).size(), fail_fast);
+ }
+
+ {
+ span<int> av = arr;
+ CHECK(av.subspan(0).size() == 5);
+ CHECK(av.subspan(1).size() == 4);
+ CHECK(av.subspan(4).size() == 1);
+ CHECK(av.subspan(5).size() == 0);
+ CHECK_THROWS_AS(av.subspan(6).size(), fail_fast);
+ const auto av2 = av.subspan(1);
+ for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+ }
+
+ {
+ span<int, 5> av = arr;
+ CHECK(av.subspan(0).size() == 5);
+ CHECK(av.subspan(1).size() == 4);
+ CHECK(av.subspan(4).size() == 1);
+ CHECK(av.subspan(5).size() == 0);
+ CHECK_THROWS_AS(av.subspan(6).size(), fail_fast);
+ const auto av2 = av.subspan(1);
+ for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+ }
+}
+
+TEST_CASE("at_call")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ span<int> s = arr;
+ CHECK(s.at(0) == 1);
+ CHECK_THROWS_AS(s.at(5), fail_fast);
+ }
+
+ {
+ int arr2d[2] = {1, 6};
+ span<int, 2> s = arr2d;
+ CHECK(s.at(0) == 1);
+ CHECK(s.at(1) == 6);
+ CHECK_THROWS_AS(s.at(2), fail_fast);
+ }
+}
+
+TEST_CASE("operator_function_call")
+{
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ span<int> s = arr;
+ CHECK(s(0) == 1);
+ CHECK_THROWS_AS(s(5), fail_fast);
+ }
+
+ {
+ int arr2d[2] = {1, 6};
+ span<int, 2> s = arr2d;
+ CHECK(s(0) == 1);
+ CHECK(s(1) == 6);
+ CHECK_THROWS_AS(s(2), fail_fast);
+ }
+}
+
+TEST_CASE("iterator_default_init")
+{
+ span<int>::iterator it1;
+ span<int>::iterator it2;
+ CHECK(it1 == it2);
+}
+
+TEST_CASE("const_iterator_default_init")
+{
+ span<int>::const_iterator it1;
+ span<int>::const_iterator it2;
+ CHECK(it1 == it2);
+}
+
+TEST_CASE("iterator_conversions")
+{
+ span<int>::iterator badIt;
+ span<int>::const_iterator badConstIt;
+ CHECK(badIt == badConstIt);
+
+ int a[] = {1, 2, 3, 4};
+ span<int> s = a;
+
+ auto it = s.begin();
+ auto cit = s.cbegin();
+
+ CHECK(it == cit);
+ CHECK(cit == it);
+
+ span<int>::const_iterator cit2 = it;
+ CHECK(cit2 == cit);
+
+ span<int>::const_iterator cit3 = it + 4;
+ CHECK(cit3 == s.cend());
+}
+
+TEST_CASE("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();
+
+ CHECK(it == cit);
+ CHECK(cit == it);
+ CHECK(it == it);
+ CHECK(cit == cit);
+ CHECK(cit == s.begin());
+ CHECK(s.begin() == cit);
+ CHECK(s.cbegin() == cit);
+ CHECK(it == s.begin());
+ CHECK(s.begin() == it);
+
+ CHECK(it != it2);
+ CHECK(it2 != it);
+ CHECK(it != s.end());
+ CHECK(it2 != s.end());
+ CHECK(s.end() != it);
+ CHECK(it2 != cit);
+ CHECK(cit != it2);
+
+ CHECK(it < it2);
+ CHECK(it <= it2);
+ CHECK(it2 <= s.end());
+ CHECK(it < s.end());
+ CHECK(it <= cit);
+ CHECK(cit <= it);
+ CHECK(cit < it2);
+ CHECK(cit <= it2);
+ CHECK(cit < s.end());
+ CHECK(cit <= s.end());
+
+ CHECK(it2 > it);
+ CHECK(it2 >= it);
+ CHECK(s.end() > it2);
+ CHECK(s.end() >= it2);
+ CHECK(it2 > cit);
+ CHECK(it2 >= cit);
+ }
+}
+
+TEST_CASE("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);
+ CHECK(it == it2);
+
+ it = s.end();
+ it2 = std::end(s);
+ CHECK(it == it2);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ span<int> s = a;
+
+ auto it = s.begin();
+ auto first = it;
+ CHECK(it == first);
+ CHECK(*it == 1);
+
+ auto beyond = s.end();
+ CHECK(it != beyond);
+ CHECK_THROWS_AS(*beyond, fail_fast);
+
+ CHECK(beyond - first == 4);
+ CHECK(first - first == 0);
+ CHECK(beyond - beyond == 0);
+
+ ++it;
+ CHECK(it - first == 1);
+ CHECK(*it == 2);
+ *it = 22;
+ CHECK(*it == 22);
+ CHECK(beyond - it == 3);
+
+ it = first;
+ CHECK(it == first);
+ while (it != s.end()) {
+ *it = 5;
+ ++it;
+ }
+
+ CHECK(it == beyond);
+ CHECK(it - beyond == 0);
+
+ for (const auto& n : s) {
+ CHECK(n == 5);
+ }
+ }
+}
+
+TEST_CASE("cbegin_cend")
+{
+ {
+ 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);
+ CHECK(cit == cit2);
+
+ cit = s.cend();
+ cit2 = std::cend(s);
+ CHECK(cit == cit2);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ span<int> s = a;
+
+ auto it = s.cbegin();
+ auto first = it;
+ CHECK(it == first);
+ CHECK(*it == 1);
+
+ auto beyond = s.cend();
+ CHECK(it != beyond);
+ CHECK_THROWS_AS(*beyond, fail_fast);
+
+ CHECK(beyond - first == 4);
+ CHECK(first - first == 0);
+ CHECK(beyond - beyond == 0);
+
+ ++it;
+ CHECK(it - first == 1);
+ CHECK(*it == 2);
+ CHECK(beyond - it == 3);
+
+ int last = 0;
+ it = first;
+ CHECK(it == first);
+ while (it != s.cend()) {
+ CHECK(*it == last + 1);
+
+ last = *it;
+ ++it;
+ }
+
+ CHECK(it == beyond);
+ CHECK(it - beyond == 0);
+ }
+}
+
+TEST_CASE("rbegin_rend")
+{
+ {
+ int a[] = {1, 2, 3, 4};
+ span<int> s = a;
+
+ auto it = s.rbegin();
+ auto first = it;
+ CHECK(it == first);
+ CHECK(*it == 4);
+
+ auto beyond = s.rend();
+ CHECK(it != beyond);
+ CHECK_THROWS_AS(*beyond, fail_fast);
+
+ CHECK(beyond - first == 4);
+ CHECK(first - first == 0);
+ CHECK(beyond - beyond == 0);
+
+ ++it;
+ CHECK(it - first == 1);
+ CHECK(*it == 3);
+ *it = 22;
+ CHECK(*it == 22);
+ CHECK(beyond - it == 3);
+
+ it = first;
+ CHECK(it == first);
+ while (it != s.rend()) {
+ *it = 5;
+ ++it;
+ }
+
+ CHECK(it == beyond);
+ CHECK(it - beyond == 0);
+
+ for (const auto& n : s) {
+ CHECK(n == 5);
+ }
+ }
+}
+
+TEST_CASE("crbegin_crend")
+{
+ {
+ int a[] = {1, 2, 3, 4};
+ span<int> s = a;
+
+ auto it = s.crbegin();
+ auto first = it;
+ CHECK(it == first);
+ CHECK(*it == 4);
+
+ auto beyond = s.crend();
+ CHECK(it != beyond);
+ CHECK_THROWS_AS(*beyond, fail_fast);
+
+ CHECK(beyond - first == 4);
+ CHECK(first - first == 0);
+ CHECK(beyond - beyond == 0);
+
+ ++it;
+ CHECK(it - first == 1);
+ CHECK(*it == 3);
+ CHECK(beyond - it == 3);
+
+ it = first;
+ CHECK(it == first);
+ int last = 5;
+ while (it != s.crend()) {
+ CHECK(*it == last - 1);
+ last = *it;
+
+ ++it;
+ }
+
+ CHECK(it == beyond);
+ CHECK(it - beyond == 0);
+ }
+}
+
+TEST_CASE("comparison_operators")
+{
+ {
+ span<int> s1;
+ span<int> s2;
+ CHECK(s1 == s2);
+ CHECK(!(s1 != s2));
+ CHECK(!(s1 < s2));
+ CHECK(s1 <= s2);
+ CHECK(!(s1 > s2));
+ CHECK(s1 >= s2);
+ CHECK(s2 == s1);
+ CHECK(!(s2 != s1));
+ CHECK(!(s2 < s1));
+ CHECK(s2 <= s1);
+ CHECK(!(s2 > s1));
+ CHECK(s2 >= s1);
+ }
+
+ {
+ int arr[] = {2, 1};
+ span<int> s1 = arr;
+ span<int> s2 = arr;
+
+ CHECK(s1 == s2);
+ CHECK(!(s1 != s2));
+ CHECK(!(s1 < s2));
+ CHECK(s1 <= s2);
+ CHECK(!(s1 > s2));
+ CHECK(s1 >= s2);
+ CHECK(s2 == s1);
+ CHECK(!(s2 != s1));
+ CHECK(!(s2 < s1));
+ CHECK(s2 <= s1);
+ CHECK(!(s2 > s1));
+ CHECK(s2 >= s1);
+ }
+
+ {
+ int arr[] = {2, 1}; // bigger
+
+ span<int> s1;
+ span<int> s2 = arr;
+
+ CHECK(s1 != s2);
+ CHECK(s2 != s1);
+ CHECK(!(s1 == s2));
+ CHECK(!(s2 == s1));
+ CHECK(s1 < s2);
+ CHECK(!(s2 < s1));
+ CHECK(s1 <= s2);
+ CHECK(!(s2 <= s1));
+ CHECK(s2 > s1);
+ CHECK(!(s1 > s2));
+ CHECK(s2 >= s1);
+ CHECK(!(s1 >= s2));
+ }
+
+ {
+ int arr1[] = {1, 2};
+ int arr2[] = {1, 2};
+ span<int> s1 = arr1;
+ span<int> s2 = arr2;
+
+ CHECK(s1 == s2);
+ CHECK(!(s1 != s2));
+ CHECK(!(s1 < s2));
+ CHECK(s1 <= s2);
+ CHECK(!(s1 > s2));
+ CHECK(s1 >= s2);
+ CHECK(s2 == s1);
+ CHECK(!(s2 != s1));
+ CHECK(!(s2 < s1));
+ CHECK(s2 <= s1);
+ CHECK(!(s2 > s1));
+ CHECK(s2 >= s1);
+ }
+
+ {
+ int arr[] = {1, 2, 3};
+
+ span<int> s1 = {&arr[0], 2}; // shorter
+ span<int> s2 = arr; // longer
+
+ CHECK(s1 != s2);
+ CHECK(s2 != s1);
+ CHECK(!(s1 == s2));
+ CHECK(!(s2 == s1));
+ CHECK(s1 < s2);
+ CHECK(!(s2 < s1));
+ CHECK(s1 <= s2);
+ CHECK(!(s2 <= s1));
+ CHECK(s2 > s1);
+ CHECK(!(s1 > s2));
+ CHECK(s2 >= s1);
+ CHECK(!(s1 >= s2));
+ }
+
+ {
+ int arr1[] = {1, 2}; // smaller
+ int arr2[] = {2, 1}; // bigger
+
+ span<int> s1 = arr1;
+ span<int> s2 = arr2;
+
+ CHECK(s1 != s2);
+ CHECK(s2 != s1);
+ CHECK(!(s1 == s2));
+ CHECK(!(s2 == s1));
+ CHECK(s1 < s2);
+ CHECK(!(s2 < s1));
+ CHECK(s1 <= s2);
+ CHECK(!(s2 <= s1));
+ CHECK(s2 > s1);
+ CHECK(!(s1 > s2));
+ CHECK(s2 >= s1);
+ CHECK(!(s1 >= s2));
+ }
+}
+
+TEST_CASE("as_bytes")
+{
+ int a[] = {1, 2, 3, 4};
+
+ {
+ const span<const int> s = a;
+ CHECK(s.size() == 4);
+ const span<const byte> bs = as_bytes(s);
+ CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+ CHECK(bs.size() == s.size_bytes());
+ }
+
+ {
+ span<int> s;
+ const auto bs = as_bytes(s);
+ CHECK(bs.size() == s.size());
+ CHECK(bs.size() == 0);
+ CHECK(bs.size_bytes() == 0);
+ CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+ CHECK(bs.data() == nullptr);
+ }
+
+ {
+ span<int> s = a;
+ const auto bs = as_bytes(s);
+ CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+ CHECK(bs.size() == s.size_bytes());
+ }
+}
+
+TEST_CASE("as_writeable_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;
+ CHECK(s.size() == 4);
+ span<const byte> bs = as_writeable_bytes(s);
+ CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
+ CHECK(bs.size() == s.size_bytes());
+#endif
+ }
+
+ {
+ span<int> s;
+ const auto bs = as_writeable_bytes(s);
+ CHECK(bs.size() == s.size());
+ CHECK(bs.size() == 0);
+ CHECK(bs.size_bytes() == 0);
+ CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
+ CHECK(bs.data() == nullptr);
+ }
+
+ {
+ span<int> s = a;
+ const auto bs = as_writeable_bytes(s);
+ CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
+ CHECK(bs.size() == s.size_bytes());
+ }
+}
+
+TEST_CASE("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;
+ CHECK(s4.size() == 4);
+
+ // converting to dynamic_range is always ok
+ {
+ span<int> s = s4;
+ CHECK(s.size() == s4.size());
+ 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
+
+ // even when done dynamically
+ {
+ span<int> s = arr;
+ auto f = [&]() {
+ span<int, 2> s2 = s;
+ static_cast<void>(s2);
+ };
+ CHECK_THROWS_AS(f(), fail_fast);
+ }
+
+ // but doing so explicitly is ok
+
+ // you can convert statically
+ {
+ const span<int, 2> s2 = {arr, 2};
+ static_cast<void>(s2);
+ }
+ {
+ const 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);
+ }
+
+ // initialization or assignment to static span that requires size INCREASE is not ok.
+ int arr2[2] = {1, 2};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ span<int, 4> s3 = arr2;
+ }
+ {
+ span<int, 2> s2 = arr2;
+ span<int, 4> s4a = s2;
+ }
+#endif
+ {
+ auto f = [&]() {
+ span<int, 4> _s4 = {arr2, 2};
+ static_cast<void>(_s4);
+ };
+ CHECK_THROWS_AS(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_THROWS_AS(f(), fail_fast);
+}
+
+TEST_CASE("interop_with_std_regex")
+{
+ char lat[] = {'1', '2', '3', '4', '5', '6', 'E', 'F', 'G'};
+ span<char> s = lat;
+ const auto f_it = s.begin() + 7;
+
+ std::match_results<span<char>::iterator> match;
+
+ std::regex_match(s.begin(), s.end(), match, std::regex(".*"));
+ CHECK(match.ready());
+ CHECK(!match.empty());
+ CHECK(match[0].matched);
+ CHECK(match[0].first == s.begin());
+ CHECK(match[0].second == s.end());
+
+ std::regex_search(s.begin(), s.end(), match, std::regex("F"));
+ CHECK(match.ready());
+ CHECK(!match.empty());
+ CHECK(match[0].matched);
+ CHECK(match[0].first == f_it);
+ CHECK(match[0].second == (f_it + 1));
+}
+
+TEST_CASE("interop_with_gsl_at")
+{
+ int arr[5] = {1, 2, 3, 4, 5};
+ span<int> s{arr};
+ CHECK((at(s, 0) == 1 && at(s, 1) == 2));
+}
+
+TEST_CASE("default_constructible")
+{
+ CHECK((std::is_default_constructible<span<int>>::value));
+ CHECK((std::is_default_constructible<span<int, 0>>::value));
+ CHECK((!std::is_default_constructible<span<int, 42>>::value));
+}
diff --git a/third_party/GSL/tests/strided_span_tests.cpp b/third_party/GSL/tests/strided_span_tests.cpp
new file mode 100644
index 0000000..8719336
--- /dev/null
+++ b/third_party/GSL/tests/strided_span_tests.cpp
@@ -0,0 +1,757 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, CHECK...
+
+#include <gsl/gsl_byte> // for byte
+#include <gsl/gsl_util> // for narrow_cast
+#include <gsl/multi_span> // for strided_span, index, multi_span, strided_...
+
+#include <iostream> // for size_t
+#include <iterator> // for begin, end
+#include <numeric> // for iota
+#include <type_traits> // for integral_constant<>::value, is_convertible
+#include <vector> // for vector
+
+namespace gsl {
+struct fail_fast;
+} // namespace gsl
+
+using namespace std;
+using namespace gsl;
+
+namespace
+{
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
+}
+
+TEST_CASE("span_section_test")
+{
+ int a[30][4][5];
+
+ const auto av = as_multi_span(a);
+ const auto sub = av.section({15, 0, 0}, gsl::multi_span_index<3>{2, 2, 2});
+ const auto subsub = sub.section({1, 0, 0}, gsl::multi_span_index<3>{1, 1, 1});
+ (void) subsub;
+}
+
+TEST_CASE("span_section")
+{
+ std::vector<int> data(5 * 10);
+ std::iota(begin(data), end(data), 0);
+ const multi_span<int, 5, 10> av = as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
+
+ const strided_span<int, 2> av_section_1 = av.section({1, 2}, {3, 4});
+ CHECK((av_section_1[{0, 0}] == 12));
+ CHECK((av_section_1[{0, 1}] == 13));
+ CHECK((av_section_1[{1, 0}] == 22));
+ CHECK((av_section_1[{2, 3}] == 35));
+
+ const strided_span<int, 2> av_section_2 = av_section_1.section({1, 2}, {2, 2});
+ CHECK((av_section_2[{0, 0}] == 24));
+ CHECK((av_section_2[{0, 1}] == 25));
+ CHECK((av_section_2[{1, 0}] == 34));
+}
+
+TEST_CASE("strided_span_constructors")
+{
+ // Check stride constructor
+ {
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ const int carr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+ strided_span<int, 1> sav1{arr, {{9}, {1}}}; // T -> T
+ CHECK(sav1.bounds().index_bounds() == multi_span_index<1>{9});
+ CHECK(sav1.bounds().stride() == 1);
+ CHECK((sav1[0] == 1 && sav1[8] == 9));
+
+ strided_span<const int, 1> sav2{carr, {{4}, {2}}}; // const T -> const T
+ CHECK(sav2.bounds().index_bounds() == multi_span_index<1>{4});
+ CHECK(sav2.bounds().strides() == multi_span_index<1>{2});
+ CHECK((sav2[0] == 1 && sav2[3] == 7));
+
+ strided_span<int, 2> sav3{arr, {{2, 2}, {6, 2}}}; // T -> const T
+ CHECK((sav3.bounds().index_bounds() == multi_span_index<2>{2, 2}));
+ CHECK((sav3.bounds().strides() == multi_span_index<2>{6, 2}));
+ CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7));
+ }
+
+ // Check multi_span constructor
+ {
+ int arr[] = {1, 2};
+
+ // From non-cv-qualified source
+ {
+ const multi_span<int> src = arr;
+
+ strided_span<int, 1> sav{src, {2, 1}};
+ CHECK(sav.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav[1] == 2);
+
+#if _MSC_VER > 1800
+ // strided_span<const int, 1> sav_c{ {src}, {2, 1} };
+ strided_span<const int, 1> sav_c{multi_span<const int>{src},
+ strided_bounds<1>{2, 1}};
+#else
+ strided_span<const int, 1> sav_c{multi_span<const int>{src},
+ strided_bounds<1>{2, 1}};
+#endif
+ CHECK(sav_c.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_c.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_c[1] == 2);
+
+#if _MSC_VER > 1800
+ strided_span<volatile int, 1> sav_v{src, {2, 1}};
+#else
+ strided_span<volatile int, 1> sav_v{multi_span<volatile int>{src},
+ strided_bounds<1>{2, 1}};
+#endif
+ CHECK(sav_v.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_v.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_v[1] == 2);
+
+#if _MSC_VER > 1800
+ strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+#else
+ strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
+ strided_bounds<1>{2, 1}};
+#endif
+ CHECK(sav_cv.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_cv.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_cv[1] == 2);
+ }
+
+ // From const-qualified source
+ {
+ const multi_span<const int> src{arr};
+
+ strided_span<const int, 1> sav_c{src, {2, 1}};
+ CHECK(sav_c.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_c.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_c[1] == 2);
+
+#if _MSC_VER > 1800
+ strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+#else
+ strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
+ strided_bounds<1>{2, 1}};
+#endif
+
+ CHECK(sav_cv.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_cv.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_cv[1] == 2);
+ }
+
+ // From volatile-qualified source
+ {
+ const multi_span<volatile int> src{arr};
+
+ strided_span<volatile int, 1> sav_v{src, {2, 1}};
+ CHECK(sav_v.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_v.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_v[1] == 2);
+
+#if _MSC_VER > 1800
+ strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+#else
+ strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
+ strided_bounds<1>{2, 1}};
+#endif
+ CHECK(sav_cv.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_cv.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_cv[1] == 2);
+ }
+
+ // From cv-qualified source
+ {
+ const multi_span<const volatile int> src{arr};
+
+ strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+ CHECK(sav_cv.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav_cv.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav_cv[1] == 2);
+ }
+ }
+
+ // Check const-casting constructor
+ {
+ int arr[2] = {4, 5};
+
+ const multi_span<int, 2> av(arr, 2);
+ multi_span<const int, 2> av2{av};
+ CHECK(av2[1] == 5);
+
+ static_assert(
+ std::is_convertible<const multi_span<int, 2>, multi_span<const int, 2>>::value,
+ "ctor is not implicit!");
+
+ const strided_span<int, 1> src{arr, {2, 1}};
+ strided_span<const int, 1> sav{src};
+ CHECK(sav.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav.bounds().stride() == 1);
+ CHECK(sav[1] == 5);
+
+ static_assert(
+ std::is_convertible<const strided_span<int, 1>, strided_span<const int, 1>>::value,
+ "ctor is not implicit!");
+ }
+
+ // Check copy constructor
+ {
+ int arr1[2] = {3, 4};
+ const strided_span<int, 1> src1{arr1, {2, 1}};
+ strided_span<int, 1> sav1{src1};
+
+ CHECK(sav1.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav1.bounds().stride() == 1);
+ CHECK(sav1[0] == 3);
+
+ int arr2[6] = {1, 2, 3, 4, 5, 6};
+ const strided_span<const int, 2> src2{arr2, {{3, 2}, {2, 1}}};
+ strided_span<const int, 2> sav2{src2};
+ CHECK((sav2.bounds().index_bounds() == multi_span_index<2>{3, 2}));
+ CHECK((sav2.bounds().strides() == multi_span_index<2>{2, 1}));
+ CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
+ }
+
+ // Check const-casting assignment operator
+ {
+ int arr1[2] = {1, 2};
+ int arr2[6] = {3, 4, 5, 6, 7, 8};
+
+ const strided_span<int, 1> src{arr1, {{2}, {1}}};
+ strided_span<const int, 1> sav{arr2, {{3}, {2}}};
+ strided_span<const int, 1>& sav_ref = (sav = src);
+ CHECK(sav.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav[0] == 1);
+ CHECK(&sav_ref == &sav);
+ }
+
+ // Check copy assignment operator
+ {
+ int arr1[2] = {3, 4};
+ int arr1b[1] = {0};
+ const strided_span<int, 1> src1{arr1, {2, 1}};
+ strided_span<int, 1> sav1{arr1b, {1, 1}};
+ strided_span<int, 1>& sav1_ref = (sav1 = src1);
+ CHECK(sav1.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav1.bounds().strides() == multi_span_index<1>{1});
+ CHECK(sav1[0] == 3);
+ CHECK(&sav1_ref == &sav1);
+
+ const int arr2[6] = {1, 2, 3, 4, 5, 6};
+ const int arr2b[1] = {0};
+ const strided_span<const int, 2> src2{arr2, {{3, 2}, {2, 1}}};
+ strided_span<const int, 2> sav2{arr2b, {{1, 1}, {1, 1}}};
+ strided_span<const int, 2>& sav2_ref = (sav2 = src2);
+ CHECK((sav2.bounds().index_bounds() == multi_span_index<2>{3, 2}));
+ CHECK((sav2.bounds().strides() == multi_span_index<2>{2, 1}));
+ CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
+ CHECK(&sav2_ref == &sav2);
+ }
+}
+
+TEST_CASE("strided_span_slice")
+{
+ std::vector<int> data(5 * 10);
+ std::iota(begin(data), end(data), 0);
+ const multi_span<int, 5, 10> src =
+ as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
+
+ const strided_span<int, 2> sav{src, {{5, 10}, {10, 1}}};
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const strided_span<const int, 2> csav{{src}, {{5, 10}, {10, 1}}};
+#endif
+ const strided_span<const int, 2> csav{multi_span<const int, 5, 10>{src},
+ {{5, 10}, {10, 1}}};
+
+ strided_span<int, 1> sav_sl = sav[2];
+ CHECK(sav_sl[0] == 20);
+ CHECK(sav_sl[9] == 29);
+
+ strided_span<const int, 1> csav_sl = sav[3];
+ CHECK(csav_sl[0] == 30);
+ CHECK(csav_sl[9] == 39);
+
+ CHECK(sav[4][0] == 40);
+ CHECK(sav[4][9] == 49);
+}
+
+TEST_CASE("strided_span_column_major")
+{
+ // strided_span may be used to accommodate more peculiar
+ // use cases, such as column-major multidimensional array
+ // (aka. "FORTRAN" layout).
+
+ int cm_array[3 * 5] = {1, 4, 7, 10, 13, 2, 5, 8, 11, 14, 3, 6, 9, 12, 15};
+ strided_span<int, 2> cm_sav{cm_array, {{5, 3}, {1, 5}}};
+
+ // Accessing elements
+ CHECK((cm_sav[{0, 0}] == 1));
+ CHECK((cm_sav[{0, 1}] == 2));
+ CHECK((cm_sav[{1, 0}] == 4));
+ CHECK((cm_sav[{4, 2}] == 15));
+
+ // Slice
+ strided_span<int, 1> cm_sl = cm_sav[3];
+
+ CHECK(cm_sl[0] == 10);
+ CHECK(cm_sl[1] == 11);
+ CHECK(cm_sl[2] == 12);
+
+ // Section
+ strided_span<int, 2> cm_sec = cm_sav.section({2, 1}, {3, 2});
+
+ CHECK((cm_sec.bounds().index_bounds() == multi_span_index<2>{3, 2}));
+ CHECK((cm_sec[{0, 0}] == 8));
+ CHECK((cm_sec[{0, 1}] == 9));
+ CHECK((cm_sec[{1, 0}] == 11));
+ CHECK((cm_sec[{2, 1}] == 15));
+}
+
+TEST_CASE("strided_span_bounds")
+{
+ int arr[] = {0, 1, 2, 3};
+ multi_span<int> av(arr);
+
+ {
+ // incorrect sections
+
+ CHECK_THROWS_AS(av.section(0, 0)[0], fail_fast);
+ CHECK_THROWS_AS(av.section(1, 0)[0], fail_fast);
+ CHECK_THROWS_AS(av.section(1, 1)[1], fail_fast);
+
+ CHECK_THROWS_AS(av.section(2, 5), fail_fast);
+ CHECK_THROWS_AS(av.section(5, 2), fail_fast);
+ CHECK_THROWS_AS(av.section(5, 0), fail_fast);
+ CHECK_THROWS_AS(av.section(0, 5), fail_fast);
+ CHECK_THROWS_AS(av.section(5, 5), fail_fast);
+ }
+
+ {
+ // zero stride
+ strided_span<int, 1> sav{av, {{4}, {}}};
+ CHECK(sav[0] == 0);
+ CHECK(sav[3] == 0);
+ CHECK_THROWS_AS(sav[4], fail_fast);
+ }
+
+ {
+ // zero extent
+ strided_span<int, 1> sav{av, {{}, {1}}};
+ CHECK_THROWS_AS(sav[0], fail_fast);
+ }
+
+ {
+ // zero extent and stride
+ strided_span<int, 1> sav{av, {{}, {}}};
+ CHECK_THROWS_AS(sav[0], fail_fast);
+ }
+
+ {
+ // strided array ctor with matching strided bounds
+ strided_span<int, 1> sav{arr, {4, 1}};
+ CHECK(sav.bounds().index_bounds() == multi_span_index<1>{4});
+ CHECK(sav[3] == 3);
+ CHECK_THROWS_AS(sav[4], fail_fast);
+ }
+
+ {
+ // strided array ctor with smaller strided bounds
+ strided_span<int, 1> sav{arr, {2, 1}};
+ CHECK(sav.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav[1] == 1);
+ CHECK_THROWS_AS(sav[2], fail_fast);
+ }
+
+ {
+ // strided array ctor with fitting irregular bounds
+ strided_span<int, 1> sav{arr, {2, 3}};
+ CHECK(sav.bounds().index_bounds() == multi_span_index<1>{2});
+ CHECK(sav[0] == 0);
+ CHECK(sav[1] == 3);
+ CHECK_THROWS_AS(sav[2], fail_fast);
+ }
+
+ {
+ // bounds cross data boundaries - from static arrays
+ CHECK_THROWS_AS((strided_span<int, 1>{arr, {3, 2}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{arr, {3, 3}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{arr, {4, 5}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{arr, {5, 1}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{arr, {5, 5}}), fail_fast);
+ }
+
+ {
+ // bounds cross data boundaries - from array view
+ CHECK_THROWS_AS((strided_span<int, 1>{av, {3, 2}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av, {3, 3}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av, {4, 5}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av, {5, 1}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av, {5, 5}}), fail_fast);
+ }
+
+ {
+ // bounds cross data boundaries - from dynamic arrays
+ CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {3, 2}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {3, 3}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {4, 5}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {5, 1}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {5, 5}}), fail_fast);
+ CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 2, {2, 2}}), fail_fast);
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ strided_span<int, 1> sav0{av.data(), {3, 2}};
+ strided_span<int, 1> sav1{arr, {1}};
+ strided_span<int, 1> sav2{arr, {1, 1, 1}};
+ strided_span<int, 1> sav3{av, {1}};
+ strided_span<int, 1> sav4{av, {1, 1, 1}};
+ strided_span<int, 2> sav5{av.as_multi_span(dim<2>(), dim<2>()), {1}};
+ strided_span<int, 2> sav6{av.as_multi_span(dim<2>(), dim<2>()), {1, 1, 1}};
+ strided_span<int, 2> sav7{av.as_multi_span(dim<2>(), dim<2>()),
+ {{1, 1}, {1, 1}, {1, 1}}};
+
+ multi_span_index<1> index{0, 1};
+ strided_span<int, 1> sav8{arr, {1, {1, 1}}};
+ strided_span<int, 1> sav9{arr, {{1, 1}, {1, 1}}};
+ strided_span<int, 1> sav10{av, {1, {1, 1}}};
+ strided_span<int, 1> sav11{av, {{1, 1}, {1, 1}}};
+ strided_span<int, 2> sav12{av.as_multi_span(dim<2>(), dim<2>()), {{1}, {1}}};
+ strided_span<int, 2> sav13{av.as_multi_span(dim<2>(), dim<2>()), {{1}, {1, 1, 1}}};
+ strided_span<int, 2> sav14{av.as_multi_span(dim<2>(), dim<2>()), {{1, 1, 1}, {1}}};
+ }
+#endif
+}
+
+TEST_CASE("strided_span_type_conversion")
+{
+ int arr[] = {0, 1, 2, 3};
+ multi_span<int> av(arr);
+
+ {
+ strided_span<int, 1> sav{av.data(), av.size(), {av.size() / 2, 2}};
+#ifdef CONFIRM_COMPILATION_ERRORS
+ strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
+#endif
+ }
+ {
+ strided_span<int, 1> sav{av, {av.size() / 2, 2}};
+#ifdef CONFIRM_COMPILATION_ERRORS
+ strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
+#endif
+ }
+
+ multi_span<const byte, dynamic_range> bytes = as_bytes(av);
+
+ // retype strided array with regular strides - from raw data
+ {
+ strided_bounds<2> bounds{{2, bytes.size() / 4}, {bytes.size() / 2, 1}};
+ strided_span<const byte, 2> sav2{bytes.data(), bytes.size(), bounds};
+ strided_span<const int, 2> sav3 = sav2.as_strided_span<const int>();
+ CHECK(sav3[0][0] == 0);
+ CHECK(sav3[1][0] == 2);
+ CHECK_THROWS_AS(sav3[1][1], fail_fast);
+ CHECK_THROWS_AS(sav3[0][1], fail_fast);
+ }
+
+ // retype strided array with regular strides - from multi_span
+ {
+ strided_bounds<2> bounds{{2, bytes.size() / 4}, {bytes.size() / 2, 1}};
+ multi_span<const byte, 2, dynamic_range> bytes2 =
+ as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+ strided_span<const byte, 2> sav2{bytes2, bounds};
+ strided_span<int, 2> sav3 = sav2.as_strided_span<int>();
+ CHECK(sav3[0][0] == 0);
+ CHECK(sav3[1][0] == 2);
+ CHECK_THROWS_AS(sav3[1][1], fail_fast);
+ CHECK_THROWS_AS(sav3[0][1], fail_fast);
+ }
+
+ // retype strided array with not enough elements - last dimension of the array is too small
+ {
+ strided_bounds<2> bounds{{4, 2}, {4, 1}};
+ multi_span<const byte, 2, dynamic_range> bytes2 =
+ as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+ strided_span<const byte, 2> sav2{bytes2, bounds};
+ CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+ }
+
+ // retype strided array with not enough elements - strides are too small
+ {
+ strided_bounds<2> bounds{{4, 2}, {2, 1}};
+ multi_span<const byte, 2, dynamic_range> bytes2 =
+ as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+ strided_span<const byte, 2> sav2{bytes2, bounds};
+ CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+ }
+
+ // retype strided array with not enough elements - last dimension does not divide by the new
+ // typesize
+ {
+ strided_bounds<2> bounds{{2, 6}, {4, 1}};
+ multi_span<const byte, 2, dynamic_range> bytes2 =
+ as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+ strided_span<const byte, 2> sav2{bytes2, bounds};
+ CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+ }
+
+ // retype strided array with not enough elements - strides does not divide by the new
+ // typesize
+ {
+ strided_bounds<2> bounds{{2, 1}, {6, 1}};
+ multi_span<const byte, 2, dynamic_range> bytes2 =
+ as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+ strided_span<const byte, 2> sav2{bytes2, bounds};
+ CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+ }
+
+ // retype strided array with irregular strides - from raw data
+ {
+ strided_bounds<1> bounds{bytes.size() / 2, 2};
+ strided_span<const byte, 1> sav2{bytes.data(), bytes.size(), bounds};
+ CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+ }
+
+ // retype strided array with irregular strides - from multi_span
+ {
+ strided_bounds<1> bounds{bytes.size() / 2, 2};
+ strided_span<const byte, 1> sav2{bytes, bounds};
+ CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+ }
+}
+
+TEST_CASE("empty_strided_spans")
+{
+ {
+ multi_span<int, 0> empty_av(nullptr);
+ strided_span<int, 1> empty_sav{empty_av, {0, 1}};
+
+ CHECK(empty_sav.bounds().index_bounds() == multi_span_index<1>{0});
+ CHECK_THROWS_AS(empty_sav[0], fail_fast);
+ CHECK_THROWS_AS(empty_sav.begin()[0], fail_fast);
+ CHECK_THROWS_AS(empty_sav.cbegin()[0], fail_fast);
+
+ for (const auto& v : empty_sav) {
+ (void) v;
+ CHECK(false);
+ }
+ }
+
+ {
+ strided_span<int, 1> empty_sav{nullptr, 0, {0, 1}};
+
+ CHECK(empty_sav.bounds().index_bounds() == multi_span_index<1>{0});
+ CHECK_THROWS_AS(empty_sav[0], fail_fast);
+ CHECK_THROWS_AS(empty_sav.begin()[0], fail_fast);
+ CHECK_THROWS_AS(empty_sav.cbegin()[0], fail_fast);
+
+ for (const auto& v : empty_sav) {
+ (void) v;
+ CHECK(false);
+ }
+ }
+}
+
+void iterate_every_other_element(multi_span<int, dynamic_range> av)
+{
+ // pick every other element
+
+ auto length = av.size() / 2;
+#if _MSC_VER > 1800
+ auto bounds = strided_bounds<1>({length}, {2});
+#else
+ auto bounds = strided_bounds<1>(multi_span_index<1>{length}, multi_span_index<1>{2});
+#endif
+ strided_span<int, 1> strided(&av.data()[1], av.size() - 1, bounds);
+
+ CHECK(strided.size() == length);
+ CHECK(strided.bounds().index_bounds()[0] == length);
+ for (auto i = 0; i < strided.size(); ++i) {
+ CHECK(strided[i] == av[2 * i + 1]);
+ }
+
+ int idx = 0;
+ for (auto num : strided) {
+ CHECK(num == av[2 * idx + 1]);
+ idx++;
+ }
+}
+
+TEST_CASE("strided_span_section_iteration")
+{
+ int arr[8] = {4, 0, 5, 1, 6, 2, 7, 3};
+
+ // static bounds
+ {
+ multi_span<int, 8> av(arr, 8);
+ iterate_every_other_element(av);
+ }
+
+ // dynamic bounds
+ {
+ multi_span<int, dynamic_range> av(arr, 8);
+ iterate_every_other_element(av);
+ }
+}
+
+TEST_CASE("dynamic_strided_span_section_iteration")
+{
+ auto arr = new int[8];
+ for (int i = 0; i < 4; ++i) {
+ arr[2 * i] = 4 + i;
+ arr[2 * i + 1] = i;
+ }
+
+ auto av = as_multi_span(arr, 8);
+ iterate_every_other_element(av);
+
+ delete[] arr;
+}
+
+void iterate_second_slice(multi_span<int, dynamic_range, dynamic_range, dynamic_range> av)
+{
+ const int expected[6] = {2, 3, 10, 11, 18, 19};
+ auto section = av.section({0, 1, 0}, {3, 1, 2});
+
+ for (auto i = 0; i < section.extent<0>(); ++i) {
+ for (auto j = 0; j < section.extent<1>(); ++j)
+ for (auto k = 0; k < section.extent<2>(); ++k) {
+ auto idx = multi_span_index<3>{i, j, k}; // avoid braces in the CHECK macro
+ CHECK(section[idx] == expected[2 * i + 2 * j + k]);
+ }
+ }
+
+ for (auto i = 0; i < section.extent<0>(); ++i) {
+ for (auto j = 0; j < section.extent<1>(); ++j)
+ for (auto k = 0; k < section.extent<2>(); ++k)
+ CHECK(section[i][j][k] == expected[2 * i + 2 * j + k]);
+ }
+
+ int i = 0;
+ for (const auto num : section) {
+ CHECK(num == expected[i]);
+ i++;
+ }
+}
+
+TEST_CASE("strided_span_section_iteration_3d")
+{
+ int arr[3][4][2]{};
+ for (auto i = 0; i < 3; ++i) {
+ for (auto j = 0; j < 4; ++j)
+ for (auto k = 0; k < 2; ++k) arr[i][j][k] = 8 * i + 2 * j + k;
+ }
+
+ {
+ multi_span<int, 3, 4, 2> av = arr;
+ iterate_second_slice(av);
+ }
+}
+
+TEST_CASE("dynamic_strided_span_section_iteration_3d")
+{
+ const auto height = 12, width = 2;
+ const auto size = height * width;
+
+ auto arr = new int[static_cast<std::size_t>(size)];
+ for (auto i = 0; i < size; ++i) {
+ arr[i] = i;
+ }
+
+ {
+ auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim<2>());
+ iterate_second_slice(av);
+ }
+
+ {
+ auto av = as_multi_span(as_multi_span(arr, 24), dim(3), dim<4>(), dim<2>());
+ iterate_second_slice(av);
+ }
+
+ {
+ auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim(4), dim<2>());
+ iterate_second_slice(av);
+ }
+
+ {
+ auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim(2));
+ iterate_second_slice(av);
+ }
+ delete[] arr;
+}
+
+TEST_CASE("strided_span_conversion")
+{
+ // get an multi_span of 'c' values from the list of X's
+
+ struct X
+ {
+ int a;
+ int b;
+ int c;
+ };
+
+ X arr[4] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}};
+
+ int s = sizeof(int) / sizeof(byte);
+ auto d2 = 3 * s;
+ auto d1 = narrow_cast<int>(sizeof(int)) * 12 / d2;
+
+ // convert to 4x12 array of bytes
+ auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim(d1), dim(d2));
+
+ CHECK(av.bounds().index_bounds()[0] == 4);
+ CHECK(av.bounds().index_bounds()[1] == 12);
+
+ // get the last 4 columns
+ auto section = av.section({0, 2 * s}, {4, s}); // { { arr[0].c[0], arr[0].c[1], arr[0].c[2],
+ // arr[0].c[3] } , { arr[1].c[0], ... } , ...
+ // }
+
+ // convert to array 4x1 array of integers
+ auto cs = section.as_strided_span<int>(); // { { arr[0].c }, {arr[1].c } , ... }
+
+ CHECK(cs.bounds().index_bounds()[0] == 4);
+ CHECK(cs.bounds().index_bounds()[1] == 1);
+
+ // transpose to 1x4 array
+ strided_bounds<2> reverse_bounds{
+ {cs.bounds().index_bounds()[1], cs.bounds().index_bounds()[0]},
+ {cs.bounds().strides()[1], cs.bounds().strides()[0]}};
+
+ strided_span<int, 2> transposed{cs.data(), cs.bounds().total_size(), reverse_bounds};
+
+ // slice to get a one-dimensional array of c's
+ strided_span<int, 1> result = transposed[0];
+
+ CHECK(result.bounds().index_bounds()[0] == 4);
+ CHECK_THROWS_AS(result.bounds().index_bounds()[1], fail_fast);
+
+ int i = 0;
+ for (auto& num : result) {
+ CHECK(num == arr[i].c);
+ i++;
+ }
+}
diff --git a/third_party/GSL/tests/string_span_tests.cpp b/third_party/GSL/tests/string_span_tests.cpp
new file mode 100644
index 0000000..c0b5b19
--- /dev/null
+++ b/third_party/GSL/tests/string_span_tests.cpp
@@ -0,0 +1,1188 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
+
+#include <gsl/gsl_assert> // for Expects, fail_fast (ptr only)
+#include <gsl/pointers> // for owner
+#include <gsl/span> // for span, dynamic_extent
+#include <gsl/string_span> // for basic_string_span, operator==, ensure_z
+
+#include <algorithm> // for move, find
+#include <cstddef> // for size_t
+#include <map> // for map
+#include <string> // for basic_string, string, char_traits, operat...
+#include <type_traits> // for remove_reference<>::type
+#include <vector> // for vector, allocator
+
+using namespace std;
+using namespace gsl;
+
+// Generic string functions
+
+namespace generic
+{
+
+template <typename CharT>
+auto strlen(const CharT* s)
+{
+ auto p = s;
+ while (*p) ++p;
+ return p - s;
+}
+
+template <typename CharT>
+auto strnlen(const CharT* s, std::size_t n)
+{
+ return std::find(s, s + n, CharT(0)) - s;
+}
+
+} // namespace generic
+
+TEST_CASE("TestLiteralConstruction")
+{
+ cwstring_span<> v = ensure_z(L"Hello");
+ CHECK(5 == v.length());
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ wstring_span<> v2 = ensure0(L"Hello");
+#endif
+}
+
+TEST_CASE("TestConstructFromStdString")
+{
+ std::string s = "Hello there world";
+ cstring_span<> v = s;
+ CHECK(v.length() == static_cast<cstring_span<>::index_type>(s.length()));
+}
+
+TEST_CASE("TestConstructFromStdVector")
+{
+ std::vector<char> vec(5, 'h');
+ string_span<> v{vec};
+ CHECK(v.length() == static_cast<string_span<>::index_type>(vec.size()));
+}
+
+TEST_CASE("TestStackArrayConstruction")
+{
+ wchar_t stack_string[] = L"Hello";
+
+ {
+ cwstring_span<> v = ensure_z(stack_string);
+ CHECK(v.length() == 5);
+ }
+
+ {
+ cwstring_span<> v = stack_string;
+ CHECK(v.length() == 5);
+ }
+
+ {
+ wstring_span<> v = ensure_z(stack_string);
+ CHECK(v.length() == 5);
+ }
+
+ {
+ wstring_span<> v = stack_string;
+ CHECK(v.length() == 5);
+ }
+}
+
+TEST_CASE("TestConstructFromConstCharPointer")
+{
+ const char* s = "Hello";
+ cstring_span<> v = ensure_z(s);
+ CHECK(v.length() == 5);
+}
+
+TEST_CASE("TestConversionToConst")
+{
+ char stack_string[] = "Hello";
+ string_span<> v = ensure_z(stack_string);
+ cstring_span<> v2 = v;
+ CHECK(v.length() == v2.length());
+}
+
+TEST_CASE("TestConversionFromConst")
+{
+ char stack_string[] = "Hello";
+ cstring_span<> v = ensure_z(stack_string);
+ (void) v;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ string_span<> v2 = v;
+ string_span<> v3 = "Hello";
+#endif
+}
+
+TEST_CASE("TestToString")
+{
+ auto s = gsl::to_string(cstring_span<>{});
+ CHECK(s.length() == 0);
+
+ char stack_string[] = "Hello";
+ cstring_span<> v = ensure_z(stack_string);
+ auto s2 = gsl::to_string(v);
+ CHECK(static_cast<cstring_span<>::index_type>(s2.length()) == v.length());
+ CHECK(s2.length() == 5);
+}
+
+TEST_CASE("TestToBasicString")
+{
+ auto s = gsl::to_basic_string<char, std::char_traits<char>, ::std::allocator<char>>(
+ cstring_span<>{});
+ CHECK(s.length() == 0);
+
+ char stack_string[] = "Hello";
+ cstring_span<> v = ensure_z(stack_string);
+ auto s2 = gsl::to_basic_string<char, std::char_traits<char>, ::std::allocator<char>>(v);
+ CHECK(static_cast<cstring_span<>::index_type>(s2.length()) == v.length());
+ CHECK(s2.length() == 5);
+}
+
+TEST_CASE("EqualityAndImplicitConstructors")
+{
+ {
+ cstring_span<> span = "Hello";
+ cstring_span<> span1;
+
+ // comparison to empty span
+ CHECK(span1 != span);
+ CHECK(span != span1);
+ }
+
+ {
+ cstring_span<> span = "Hello";
+ cstring_span<> span1 = "Hello1";
+
+ // comparison to different span
+ CHECK(span1 != span);
+ CHECK(span != span1);
+ }
+
+ {
+ cstring_span<> span = "Hello";
+
+ const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ const char ar1[] = "Hello";
+ const char ar2[10] = "Hello";
+ const char* ptr = "Hello";
+ const std::string str = "Hello";
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ gsl::span<const char> sp = ensure_z("Hello");
+
+ // comparison to literal
+ CHECK(span == cstring_span<>("Hello"));
+
+ // comparison to static array with no null termination
+ CHECK(span == cstring_span<>(ar));
+
+ // comparison to static array with null at the end
+ CHECK(span == cstring_span<>(ar1));
+
+ // comparison to static array with null in the middle
+ CHECK(span == cstring_span<>(ar2));
+
+ // comparison to null-terminated c string
+ CHECK(span == cstring_span<>(ptr, 5));
+
+ // comparison to string
+ CHECK(span == cstring_span<>(str));
+
+ // comparison to vector of charaters with no null termination
+ CHECK(span == cstring_span<>(vec));
+
+ // comparison to span
+ CHECK(span == cstring_span<>(sp));
+
+ // comparison to string_span
+ CHECK(span == span);
+ }
+
+ {
+ char ar[] = {'H', 'e', 'l', 'l', 'o'};
+
+ string_span<> span = ar;
+
+ char ar1[] = "Hello";
+ char ar2[10] = "Hello";
+ char* ptr = ar;
+ std::string str = "Hello";
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ gsl::span<char> sp = ensure_z(ar1);
+
+ // comparison to static array with no null termination
+ CHECK(span == string_span<>(ar));
+
+ // comparison to static array with null at the end
+ CHECK(span == string_span<>(ar1));
+
+ // comparison to static array with null in the middle
+ CHECK(span == string_span<>(ar2));
+
+ // comparison to null-terminated c string
+ CHECK(span == string_span<>(ptr, 5));
+
+ // comparison to string
+ CHECK(span == string_span<>(str));
+
+ // comparison to vector of charaters with no null termination
+ CHECK(span == string_span<>(vec));
+
+ // comparison to span
+ CHECK(span == string_span<>(sp));
+
+ // comparison to string_span
+ CHECK(span == span);
+ }
+
+ {
+ const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ const char ar1[] = "Hello";
+ const char ar2[10] = "Hello";
+ const std::string str = "Hello";
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const gsl::span<const char> sp = ensure_z("Hello");
+
+ cstring_span<> span = "Hello";
+
+ // const span, const other type
+
+ CHECK(span == "Hello");
+ CHECK(span == ar);
+ CHECK(span == ar1);
+ CHECK(span == ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const char* ptr = "Hello";
+ CHECK(span == ptr);
+#endif
+ CHECK(span == str);
+ CHECK(span == vec);
+ CHECK(span == sp);
+
+ CHECK("Hello" == span);
+ CHECK(ar == span);
+ CHECK(ar1 == span);
+ CHECK(ar2 == span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(ptr == span);
+#endif
+ CHECK(str == span);
+ CHECK(vec == span);
+ CHECK(sp == span);
+
+ // const span, non-const other type
+
+ char _ar[] = {'H', 'e', 'l', 'l', 'o'};
+ char _ar1[] = "Hello";
+ char _ar2[10] = "Hello";
+ char* _ptr = _ar;
+ std::string _str = "Hello";
+ std::vector<char> _vec = {'H', 'e', 'l', 'l', 'o'};
+ gsl::span<char> _sp{_ar, 5};
+
+ CHECK(span == _ar);
+ CHECK(span == _ar1);
+ CHECK(span == _ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(span == _ptr);
+#endif
+ CHECK(span == _str);
+ CHECK(span == _vec);
+ CHECK(span == _sp);
+
+ CHECK(_ar == span);
+ CHECK(_ar1 == span);
+ CHECK(_ar2 == span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(_ptr == span);
+#endif
+ CHECK(_str == span);
+ CHECK(_vec == span);
+ CHECK(_sp == span);
+
+ string_span<> _span{_ptr, 5};
+
+ // non-const span, non-const other type
+
+ CHECK(_span == _ar);
+ CHECK(_span == _ar1);
+ CHECK(_span == _ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(_span == _ptr);
+#endif
+ CHECK(_span == _str);
+ CHECK(_span == _vec);
+ CHECK(_span == _sp);
+
+ CHECK(_ar == _span);
+ CHECK(_ar1 == _span);
+ CHECK(_ar2 == _span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(_ptr == _span);
+#endif
+ CHECK(_str == _span);
+ CHECK(_vec == _span);
+ CHECK(_sp == _span);
+
+ // non-const span, const other type
+
+ CHECK(_span == "Hello");
+ CHECK(_span == ar);
+ CHECK(_span == ar1);
+ CHECK(_span == ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(_span == ptr);
+#endif
+ CHECK(_span == str);
+ CHECK(_span == vec);
+ CHECK(_span == sp);
+
+ CHECK("Hello" == _span);
+ CHECK(ar == _span);
+ CHECK(ar1 == _span);
+ CHECK(ar2 == _span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(ptr == _span);
+#endif
+ CHECK(str == _span);
+ CHECK(vec == _span);
+ CHECK(sp == _span);
+
+ // two spans
+
+ CHECK(_span == span);
+ CHECK(span == _span);
+ }
+
+ {
+ std::vector<char> str1 = {'H', 'e', 'l', 'l', 'o'};
+ cstring_span<> span1 = str1;
+ std::vector<char> str2 = std::move(str1);
+ cstring_span<> span2 = str2;
+
+ // comparison of spans from the same vector before and after move (ok)
+ CHECK(span1 == span2);
+ }
+}
+
+TEST_CASE("ComparisonAndImplicitConstructors")
+{
+ {
+ cstring_span<> span = "Hello";
+
+ const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ const char ar1[] = "Hello";
+ const char ar2[10] = "Hello";
+ const char* ptr = "Hello";
+ const std::string str = "Hello";
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+
+ // comparison to literal
+ CHECK(span < cstring_span<>("Helloo"));
+ CHECK(span > cstring_span<>("Hell"));
+
+ // comparison to static array with no null termination
+ CHECK(span >= cstring_span<>(ar));
+
+ // comparison to static array with null at the end
+ CHECK(span <= cstring_span<>(ar1));
+
+ // comparison to static array with null in the middle
+ CHECK(span >= cstring_span<>(ar2));
+
+ // comparison to null-terminated c string
+ CHECK(span <= cstring_span<>(ptr, 5));
+
+ // comparison to string
+ CHECK(span >= cstring_span<>(str));
+
+ // comparison to vector of charaters with no null termination
+ CHECK(span <= cstring_span<>(vec));
+ }
+
+ {
+ char ar[] = {'H', 'e', 'l', 'l', 'o'};
+
+ string_span<> span = ar;
+
+ char larr[] = "Hell";
+ char rarr[] = "Helloo";
+
+ char ar1[] = "Hello";
+ char ar2[10] = "Hello";
+ char* ptr = ar;
+ std::string str = "Hello";
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+
+ // comparison to static array with no null termination
+ CHECK(span <= string_span<>(ar));
+ CHECK(span < string_span<>(rarr));
+ CHECK(span > string_span<>(larr));
+
+ // comparison to static array with null at the end
+ CHECK(span >= string_span<>(ar1));
+
+ // comparison to static array with null in the middle
+ CHECK(span <= string_span<>(ar2));
+
+ // comparison to null-terminated c string
+ CHECK(span >= string_span<>(ptr, 5));
+
+ // comparison to string
+ CHECK(span <= string_span<>(str));
+
+ // comparison to vector of charaters with no null termination
+ CHECK(span >= string_span<>(vec));
+ }
+}
+TEST_CASE("ConstrutorsEnsureZ")
+{
+ // remove z from literals
+ {
+ cstring_span<> sp = "hello";
+ CHECK((sp.length() == 5));
+ }
+
+ // take the string as is
+ {
+ auto str = std::string("hello");
+ cstring_span<> sp = str;
+ CHECK((sp.length() == 5));
+ }
+
+ // ensure z on c strings
+ {
+ gsl::owner<char*> ptr = new char[3];
+
+ ptr[0] = 'a';
+ ptr[1] = 'b';
+ ptr[2] = '\0';
+
+ string_span<> span = ensure_z(ptr);
+ CHECK(span.length() == 2);
+
+ delete[] ptr;
+ }
+}
+
+TEST_CASE("Constructors")
+{
+ // creating cstring_span
+
+ // from span of a final extent
+ {
+ span<const char, 6> sp = "Hello";
+ cstring_span<> span = sp;
+ CHECK(span.length() == 6);
+ }
+
+// from const span of a final extent to non-const string_span
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ span<const char, 6> sp = "Hello";
+ string_span<> span = sp;
+ CHECK(span.length() == 6);
+ }
+#endif
+
+// from string temporary
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ cstring_span<> span = std::string("Hello");
+ }
+#endif
+
+ // default
+ {
+ cstring_span<> span;
+ CHECK(span.length() == 0);
+ }
+
+ // from string literal
+ {
+ cstring_span<> span = "Hello";
+ CHECK(span.length() == 5);
+ }
+
+ // from const static array
+ {
+ const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ cstring_span<> span = ar;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const static array
+ {
+ char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ cstring_span<> span = ar;
+ CHECK(span.length() == 5);
+ }
+
+ // from const ptr and length
+ {
+ const char* ptr = "Hello";
+ cstring_span<> span{ptr, 5};
+ CHECK(span.length() == 5);
+ }
+
+ // from const ptr and length, include 0
+ {
+ const char* ptr = "Hello";
+ cstring_span<> span{ptr, 6};
+ CHECK(span.length() == 6);
+ }
+
+ // from const ptr and length, 0 inside
+ {
+ const char* ptr = "He\0lo";
+ cstring_span<> span{ptr, 5};
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const ptr and length
+ {
+ char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ char* ptr = ar;
+ cstring_span<> span{ptr, 5};
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const ptr and length, 0 inside
+ {
+ char ar[] = {'H', 'e', '\0', 'l', 'o'};
+ char* ptr = ar;
+ cstring_span<> span{ptr, 5};
+ CHECK(span.length() == 5);
+ }
+
+ // from const string
+ {
+ const std::string str = "Hello";
+ const cstring_span<> span = str;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const string
+ {
+ std::string str = "Hello";
+ const cstring_span<> span = str;
+ CHECK(span.length() == 5);
+ }
+
+ // from const vector
+ {
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const cstring_span<> span = vec;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const vector
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const cstring_span<> span = vec;
+ CHECK(span.length() == 5);
+ }
+
+ // from const span
+ {
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const span<const char> inner = vec;
+ const cstring_span<> span = inner;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const span
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const span<char> inner = vec;
+ const cstring_span<> span = inner;
+ CHECK(span.length() == 5);
+ }
+
+ // from const string_span
+ {
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const cstring_span<> tmp = vec;
+ const cstring_span<> span = tmp;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const string_span
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> tmp = vec;
+ cstring_span<> span = tmp;
+ CHECK(span.length() == 5);
+ }
+
+ // creating string_span
+
+ // from string literal
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ string_span<> span = "Hello";
+#endif
+ }
+
+ // from const static array
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> span = ar;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from non-const static array
+ {
+ char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> span = ar;
+ CHECK(span.length() == 5);
+ }
+
+ // from const ptr and length
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const char* ptr = "Hello";
+ string_span<> span{ptr, 5};
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from non-const ptr and length
+ {
+ char ar[] = {'H', 'e', 'l', 'l', 'o'};
+ char* ptr = ar;
+ string_span<> span{ptr, 5};
+ CHECK(span.length() == 5);
+ }
+
+ // from const string
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const std::string str = "Hello";
+ string_span<> span = str;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from non-const string
+ {
+ std::string str = "Hello";
+ string_span<> span = str;
+ CHECK(span.length() == 5);
+ }
+
+ // from const vector
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> span = vec;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from non-const vector
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> span = vec;
+ CHECK(span.length() == 5);
+ }
+
+ // from const span
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const span<const char> inner = vec;
+ string_span<> span = inner;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from non-const span
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ span<char> inner = vec;
+ string_span<> span = inner;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const span of non-const data from const vector
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const span<char> inner = vec;
+ string_span<> span = inner;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from const string_span
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ cstring_span<> tmp = vec;
+ string_span<> span = tmp;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from non-const string_span
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const string_span<> tmp = vec;
+ const string_span<> span = tmp;
+ CHECK(span.length() == 5);
+ }
+
+ // from non-const string_span from const vector
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> tmp = vec;
+ string_span<> span = tmp;
+ CHECK(span.length() == 5);
+#endif
+ }
+
+ // from const string_span of non-const data
+ {
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ const string_span<> tmp = vec;
+ const string_span<> span = tmp;
+ CHECK(span.length() == 5);
+ }
+}
+
+template <typename T>
+T move_wrapper(T&& t)
+{
+ return std::move(t);
+}
+
+template <class T>
+T create()
+{
+ return T{};
+}
+
+template <class T>
+void use(basic_string_span<T, gsl::dynamic_extent>)
+{
+}
+
+TEST_CASE("MoveConstructors")
+{
+ // move string_span
+ {
+ cstring_span<> span = "Hello";
+ const auto span1 = std::move(span);
+ CHECK(span1.length() == 5);
+ }
+ {
+ cstring_span<> span = "Hello";
+ const auto span1 = move_wrapper(std::move(span));
+ CHECK(span1.length() == 5);
+ }
+ {
+ cstring_span<> span = "Hello";
+ const auto span1 = move_wrapper(std::move(span));
+ CHECK(span1.length() == 5);
+ }
+
+ // move span
+ {
+ span<const char> span = ensure_z("Hello");
+ const cstring_span<> span1 = std::move(span);
+ CHECK(span1.length() == 5);
+ }
+ {
+ span<const char> span = ensure_z("Hello");
+ const cstring_span<> span2 = move_wrapper(std::move(span));
+ CHECK(span2.length() == 5);
+ }
+
+ // move string
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::string str = "Hello";
+ string_span<> span = std::move(str);
+ CHECK(span.length() == 5);
+#endif
+ }
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::string str = "Hello";
+ string_span<> span = move_wrapper<std::string>(std::move(str));
+ CHECK(span.length() == 5);
+#endif
+ }
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ use<char>(create<string>());
+#endif
+ }
+
+ // move container
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> span = std::move(vec);
+ CHECK(span.length() == 5);
+#endif
+ }
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+ string_span<> span = move_wrapper<std::vector<char>>(std::move(vec));
+ CHECK(span.length() == 5);
+#endif
+ }
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ use<char>(create<std::vector<char>>());
+#endif
+ }
+}
+
+TEST_CASE("Conversion")
+{
+#ifdef CONFIRM_COMPILATION_ERRORS
+ cstring_span<> span = "Hello";
+ cwstring_span<> wspan{span};
+ CHECK(wspan.length() == 5);
+#endif
+}
+
+czstring_span<> CreateTempName(string_span<> span)
+{
+ Expects(span.size() > 1);
+
+ int last = 0;
+ if (span.size() > 4) {
+ span[0] = 't';
+ span[1] = 'm';
+ span[2] = 'p';
+ last = 3;
+ }
+ span[last] = '\0';
+
+ auto ret = span.subspan(0, 4);
+ return {ret};
+}
+
+TEST_CASE("zstring")
+{
+
+ // create zspan from zero terminated string
+ {
+ char buf[1];
+ buf[0] = '\0';
+
+ zstring_span<> zspan({buf, 1});
+
+ CHECK(generic::strlen(zspan.assume_z()) == 0);
+ CHECK(zspan.as_string_span().size() == 0);
+ CHECK(zspan.ensure_z().size() == 0);
+ }
+
+ // create zspan from non-zero terminated string
+ {
+ char buf[1];
+ buf[0] = 'a';
+
+ auto workaround_macro = [&]() { zstring_span<> zspan({buf, 1}); };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ // usage scenario: create zero-terminated temp file name and pass to a legacy API
+ {
+ char buf[10];
+
+ auto name = CreateTempName({buf, 10});
+ if (!name.empty()) {
+ czstring<> str = name.assume_z();
+ CHECK(generic::strlen(str) == 3);
+ CHECK(*(str + 3) == '\0');
+ }
+ }
+}
+
+cwzstring_span<> CreateTempNameW(wstring_span<> span)
+{
+ Expects(span.size() > 1);
+
+ int last = 0;
+ if (span.size() > 4) {
+ span[0] = L't';
+ span[1] = L'm';
+ span[2] = L'p';
+ last = 3;
+ }
+ span[last] = L'\0';
+
+ auto ret = span.subspan(0, 4);
+ return {ret};
+}
+
+TEST_CASE("wzstring")
+{
+
+ // create zspan from zero terminated string
+ {
+ wchar_t buf[1];
+ buf[0] = L'\0';
+
+ wzstring_span<> zspan({buf, 1});
+
+ CHECK(generic::strnlen(zspan.assume_z(), 1) == 0);
+ CHECK(zspan.as_string_span().size() == 0);
+ CHECK(zspan.ensure_z().size() == 0);
+ }
+
+ // create zspan from non-zero terminated string
+ {
+ wchar_t buf[1];
+ buf[0] = L'a';
+
+ const auto workaround_macro = [&]() { wzstring_span<> zspan({buf, 1}); };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ // usage scenario: create zero-terminated temp file name and pass to a legacy API
+ {
+ wchar_t buf[10];
+
+ const auto name = CreateTempNameW({buf, 10});
+ if (!name.empty()) {
+ cwzstring<> str = name.assume_z();
+ CHECK(generic::strnlen(str, 10) == 3);
+ CHECK(*(str + 3) == L'\0');
+ }
+ }
+}
+
+cu16zstring_span<> CreateTempNameU16(u16string_span<> span)
+{
+ Expects(span.size() > 1);
+
+ int last = 0;
+ if (span.size() > 4) {
+ span[0] = u't';
+ span[1] = u'm';
+ span[2] = u'p';
+ last = 3;
+ }
+ span[last] = u'\0';
+
+ auto ret = span.subspan(0, 4);
+ return {ret};
+}
+
+TEST_CASE("u16zstring")
+{
+
+ // create zspan from zero terminated string
+ {
+ char16_t buf[1];
+ buf[0] = L'\0';
+
+ u16zstring_span<> zspan({buf, 1});
+
+ CHECK(generic::strnlen(zspan.assume_z(), 1) == 0);
+ CHECK(zspan.as_string_span().size() == 0);
+ CHECK(zspan.ensure_z().size() == 0);
+ }
+
+ // create zspan from non-zero terminated string
+ {
+ char16_t buf[1];
+ buf[0] = u'a';
+
+ const auto workaround_macro = [&]() { u16zstring_span<> zspan({buf, 1}); };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ // usage scenario: create zero-terminated temp file name and pass to a legacy API
+ {
+ char16_t buf[10];
+
+ const auto name = CreateTempNameU16({buf, 10});
+ if (!name.empty()) {
+ cu16zstring<> str = name.assume_z();
+ CHECK(generic::strnlen(str, 10) == 3);
+ CHECK(*(str + 3) == L'\0');
+ }
+ }
+}
+
+cu32zstring_span<> CreateTempNameU32(u32string_span<> span)
+{
+ Expects(span.size() > 1);
+
+ int last = 0;
+ if (span.size() > 4) {
+ span[0] = U't';
+ span[1] = U'm';
+ span[2] = U'p';
+ last = 3;
+ }
+ span[last] = U'\0';
+
+ auto ret = span.subspan(0, 4);
+ return {ret};
+}
+
+TEST_CASE("u32zstring")
+{
+
+ // create zspan from zero terminated string
+ {
+ char32_t buf[1];
+ buf[0] = L'\0';
+
+ u32zstring_span<> zspan({buf, 1});
+
+ CHECK(generic::strnlen(zspan.assume_z(), 1) == 0);
+ CHECK(zspan.as_string_span().size() == 0);
+ CHECK(zspan.ensure_z().size() == 0);
+ }
+
+ // create zspan from non-zero terminated string
+ {
+ char32_t buf[1];
+ buf[0] = u'a';
+
+ const auto workaround_macro = [&]() { u32zstring_span<> zspan({buf, 1}); };
+ CHECK_THROWS_AS(workaround_macro(), fail_fast);
+ }
+
+ // usage scenario: create zero-terminated temp file name and pass to a legacy API
+ {
+ char32_t buf[10];
+
+ const auto name = CreateTempNameU32({buf, 10});
+ if (!name.empty()) {
+ cu32zstring<> str = name.assume_z();
+ CHECK(generic::strnlen(str, 10) == 3);
+ CHECK(*(str + 3) == L'\0');
+ }
+ }
+}
+
+TEST_CASE("Issue305")
+{
+ std::map<gsl::cstring_span<>, int> foo = {{"foo", 0}, {"bar", 1}};
+ CHECK(foo["foo"] == 0);
+ CHECK(foo["bar"] == 1);
+}
+
+TEST_CASE("char16_t type")
+{
+ gsl::cu16string_span<> ss1 = gsl::ensure_z(u"abc");
+ CHECK(ss1.size() == 3);
+ CHECK(ss1.size_bytes() == 6);
+
+ std::u16string s1 = gsl::to_string(ss1);
+ CHECK(s1 == u"abc");
+
+ std::u16string s2 = u"abc";
+ gsl::u16string_span<> ss2 = s2;
+ CHECK(ss2.size() == 3);
+
+ gsl::u16string_span<> ss3 = ss2.subspan(1, 1);
+ CHECK(ss3.size() == 1);
+ CHECK(ss3[0] == u'b');
+
+ char16_t buf[4]{u'a', u'b', u'c', u'\0'};
+ gsl::u16string_span<> ss4{buf, 4};
+ CHECK(ss4[3] == u'\0');
+
+ gsl::cu16zstring_span<> ss5(u"abc");
+ CHECK(ss5.as_string_span().size() == 3);
+
+ gsl::cu16string_span<> ss6 = ss5.as_string_span();
+ CHECK(ss6 == ss1);
+
+ std::vector<char16_t> v7 = {u'a', u'b', u'c'};
+ gsl::cu16string_span<> ss7{v7};
+ CHECK(ss7 == ss1);
+
+ gsl::cu16string_span<> ss8 = gsl::ensure_z(u"abc");
+ gsl::cu16string_span<> ss9 = gsl::ensure_z(u"abc");
+ CHECK(ss8 == ss9);
+
+ ss9 = gsl::ensure_z(u"abd");
+ CHECK(ss8 < ss9);
+ CHECK(ss8 <= ss9);
+ CHECK(ss8 != ss9);
+}
+
+TEST_CASE("char32_t type")
+{
+ gsl::cu32string_span<> ss1 = gsl::ensure_z(U"abc");
+ CHECK(ss1.size() == 3);
+ CHECK(ss1.size_bytes() == 12);
+
+ std::u32string s1 = gsl::to_string(ss1);
+ CHECK(s1 == U"abc");
+
+ std::u32string s2 = U"abc";
+ gsl::u32string_span<> ss2 = s2;
+ CHECK(ss2.size() == 3);
+
+ gsl::u32string_span<> ss3 = ss2.subspan(1, 1);
+ CHECK(ss3.size() == 1);
+ CHECK(ss3[0] == U'b');
+
+ char32_t buf[4]{U'a', U'b', U'c', U'\0'};
+ gsl::u32string_span<> ss4{buf, 4};
+ CHECK(ss4[3] == u'\0');
+
+ gsl::cu32zstring_span<> ss5(U"abc");
+ CHECK(ss5.as_string_span().size() == 3);
+
+ gsl::cu32string_span<> ss6 = ss5.as_string_span();
+ CHECK(ss6 == ss1);
+
+ gsl::cu32string_span<> ss8 = gsl::ensure_z(U"abc");
+ gsl::cu32string_span<> ss9 = gsl::ensure_z(U"abc");
+ CHECK(ss8 == ss9);
+
+ ss9 = gsl::ensure_z(U"abd");
+ CHECK(ss8 < ss9);
+ CHECK(ss8 <= ss9);
+ CHECK(ss8 != ss9);
+}
+
+TEST_CASE("as_bytes")
+{
+ cwzstring_span<> v(L"qwerty");
+ const auto s = v.as_string_span();
+ const auto bs = as_bytes(s);
+ CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+ CHECK(bs.size() == s.size_bytes());
+}
+
+TEST_CASE("as_writeable_bytes")
+{
+ wchar_t buf[]{L"qwerty"};
+ wzstring_span<> v(buf);
+ const auto s = v.as_string_span();
+ const auto bs = as_writeable_bytes(s);
+ CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+ CHECK(bs.size() == s.size_bytes());
+}
diff --git a/third_party/GSL/tests/test.cpp b/third_party/GSL/tests/test.cpp
new file mode 100644
index 0000000..bae194d
--- /dev/null
+++ b/third_party/GSL/tests/test.cpp
@@ -0,0 +1,18 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#define CATCH_CONFIG_MAIN
+#include <catch/catch.hpp>
diff --git a/third_party/GSL/tests/utils_tests.cpp b/third_party/GSL/tests/utils_tests.cpp
new file mode 100644
index 0000000..587b0a1
--- /dev/null
+++ b/third_party/GSL/tests/utils_tests.cpp
@@ -0,0 +1,120 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
+
+#include <gsl/gsl_util> // for narrow, finally, narrow_cast, narrowing_e...
+
+#include <algorithm> // for move
+#include <functional> // for reference_wrapper, _Bind_helper<>::type
+#include <limits> // for numeric_limits
+#include <stdint.h> // for uint32_t, int32_t
+#include <type_traits> // for is_same
+
+using namespace gsl;
+
+TEST_CASE("sanity check for gsl::index typedef")
+{
+ static_assert(std::is_same<gsl::index, std::ptrdiff_t>::value,
+ "gsl::index represents wrong arithmetic type");
+}
+
+void f(int& i) { i += 1; }
+
+TEST_CASE("finally_lambda")
+{
+ int i = 0;
+ {
+ auto _ = finally([&]() { f(i); });
+ CHECK(i == 0);
+ }
+ CHECK(i == 1);
+}
+
+TEST_CASE("finally_lambda_move")
+{
+ int i = 0;
+ {
+ auto _1 = finally([&]() { f(i); });
+ {
+ auto _2 = std::move(_1);
+ CHECK(i == 0);
+ }
+ CHECK(i == 1);
+ {
+ auto _2 = std::move(_1);
+ CHECK(i == 1);
+ }
+ CHECK(i == 1);
+ }
+ CHECK(i == 1);
+}
+
+TEST_CASE("finally_function_with_bind")
+{
+ int i = 0;
+ {
+ auto _ = finally(std::bind(&f, std::ref(i)));
+ CHECK(i == 0);
+ }
+ CHECK(i == 1);
+}
+
+int j = 0;
+void g() { j += 1; }
+TEST_CASE("finally_function_ptr")
+{
+ j = 0;
+ {
+ auto _ = finally(&g);
+ CHECK(j == 0);
+ }
+ CHECK(j == 1);
+}
+
+TEST_CASE("narrow_cast")
+{
+ int n = 120;
+ char c = narrow_cast<char>(n);
+ CHECK(c == 120);
+
+ n = 300;
+ unsigned char uc = narrow_cast<unsigned char>(n);
+ CHECK(uc == 44);
+}
+
+TEST_CASE("narrow")
+{
+ int n = 120;
+ const char c = narrow<char>(n);
+ CHECK(c == 120);
+
+ n = 300;
+ CHECK_THROWS_AS(narrow<char>(n), narrowing_error);
+
+ const auto int32_max = std::numeric_limits<int32_t>::max();
+ const auto int32_min = std::numeric_limits<int32_t>::min();
+
+ CHECK(narrow<uint32_t>(int32_t(0)) == 0);
+ CHECK(narrow<uint32_t>(int32_t(1)) == 1);
+ CHECK(narrow<uint32_t>(int32_max) == static_cast<uint32_t>(int32_max));
+
+ CHECK_THROWS_AS(narrow<uint32_t>(int32_t(-1)), narrowing_error);
+ CHECK_THROWS_AS(narrow<uint32_t>(int32_min), narrowing_error);
+
+ n = -42;
+ CHECK_THROWS_AS(narrow<unsigned>(n), narrowing_error);
+}