From 9f56f633f15e0dc52cc1dcc5d28f237e04aa92f8 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 17 Apr 2021 13:43:56 +0200 Subject: [PATCH 1/6] dep: Add span-lite as C++20 span implementation Link to the project https://github.com/martinmoene/span-lite --- dep/CMakeLists.txt | 1 + dep/span-lite/CMakeLists.txt | 9 + dep/span-lite/LICENSE.txt | 23 + dep/span-lite/README.md | 514 +++++++++ dep/span-lite/include/span.hpp | 1817 ++++++++++++++++++++++++++++++++ 5 files changed, 2364 insertions(+) create mode 100644 dep/span-lite/CMakeLists.txt create mode 100644 dep/span-lite/LICENSE.txt create mode 100644 dep/span-lite/README.md create mode 100644 dep/span-lite/include/span.hpp diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index bb197570c..792048adf 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(jwt-cpp) add_subdirectory(libbcrypt) add_subdirectory(RtspServer) +add_subdirectory(span-lite) diff --git a/dep/span-lite/CMakeLists.txt b/dep/span-lite/CMakeLists.txt new file mode 100644 index 000000000..d63cc9a4a --- /dev/null +++ b/dep/span-lite/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(span-lite INTERFACE) +add_library(martinmoene::span-lite ALIAS span-lite) + +target_include_directories(span-lite INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_link_libraries(span-lite + INTERFACE + zm-dependency-interface) diff --git a/dep/span-lite/LICENSE.txt b/dep/span-lite/LICENSE.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/dep/span-lite/LICENSE.txt @@ -0,0 +1,23 @@ +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. diff --git a/dep/span-lite/README.md b/dep/span-lite/README.md new file mode 100644 index 000000000..4b077c889 --- /dev/null +++ b/dep/span-lite/README.md @@ -0,0 +1,514 @@ + +# span lite: A single-file header-only version of a C++20-like span for C++98, C++11 and later + +[![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://travis-ci.org/martinmoene/span-lite.svg?branch=master)](https://travis-ci.org/martinmoene/span-lite) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/span-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fspan-lite.svg)](https://github.com/martinmoene/span-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/span-lite/blob/master/include/nonstd/span.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/span-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/venR3Ko2Q4tlvcVk) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/htwpnb) + +**Contents** + +- [Example usage](#example-usage) +- [In a nutshell](#in-a-nutshell) +- [License](#license) +- [Dependencies](#dependencies) +- [Installation and use](#installation-and-use) +- [Synopsis](#synopsis) +- [Reported to work with](#reported-to-work-with) +- [Building the tests](#building-the-tests) +- [Other implementations of span](#other-implementations-of-span) +- [Notes and references](#notes-and-references) +- [Appendix](#appendix) + +## Example usage + +```cpp +#include "nonstd/span.hpp" +#include +#include +#include + +std::ptrdiff_t size( nonstd::span spn ) +{ + return spn.size(); +} + +int main() +{ + int arr[] = { 1, }; + + std::cout << + "C-array:" << size( arr ) << + " array:" << size( std::array { 1, 2, } ) << + " vector:" << size( std::vector{ 1, 2, 3, } ); +} +``` + +### Compile and run + +```bash +prompt> g++ -std=c++11 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe +C-array:1 array:2 vector:3 +``` + +## In a nutshell + +**span lite** is a single-file header-only library to provide a bounds-safe view for sequences of objects. The library provides a [C++20-like span](http://en.cppreference.com/w/cpp/container/span) for use with C++98 and later. If available, `std::span` is used, unless [configured otherwise](#configuration). *span-lite* can detect the presence of [*byte-lite*](https://github.com/martinmoene/byte-lite) and if present, it provides `as_bytes()` and `as_writable_bytes()` also for C++14 and earlier. + +**Features and properties of span lite** are ease of installation (single header), freedom of dependencies other than the standard library. To compensate for the class template argument deduction that is missing from pre-C++17 compilers, `nonstd::span` can provide `make_span` functions. See [configuration](#configuration). + +## License + +*span lite* is distributed under the [Boost Software License](https://github.com/martinmoene/span-lite/blob/master/LICENSE.txt). + +## Dependencies + +*span lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). + +## Installation and use + +*span lite* is a single-file header-only library. Put `span.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. + +## Synopsis + +**Contents** +[Documentation of `std::span`](#documentation-of-stdspan) +[Later additions](#later-additions) +[Non-standard extensions](#non-standard-extensions) +[Configuration](#configuration) + +## Documentation of `std::span` + +Depending on the compiler and C++-standard used, `nonstd::span` behaves less or more like `std::span`. To get an idea of the capabilities of `nonstd::span` with your configuration, look at the output of the [tests](test/span.t.cpp), issuing `span-main.t --pass @`. For `std::span`, see its [documentation at cppreference](http://en.cppreference.com/w/cpp/container/span). + +## Later additions + +### `back()` and `front()` + +*span lite* can provide `back()` and `front()` member functions for element access. See the table below and section [configuration](#configuration). + +## Non-standard extensions + +### Construct from container + +To construct a span from a container with compilers that cannot constrain such a single-parameter constructor to containers, *span lite* provides a constructor that takes an additional parameter of type `with_container_t`. Use `with_container` as value for this parameter. See the table below and section [configuration](#configuration). + +### Construct from `std::array` with const data + +*span lite* can provide construction of a span from a `std::array` with const data. See the table below and section [configuration](#configuration). + +### `operator()` + +*span lite* can provide member function call `operator()` for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. + +### `at()` + +*span lite* can provide member function `at()` for element access. Unless exceptions have been disabled, `at()` throws std::out_of_range if the index falls outside the span. With exceptions disabled, `at(index_t)` delegates bounds checking to `operator[](index_t)`. See the table below and sections [configuration](#configuration) and [disable exceptions](#disable-exceptions). + +### `swap()` + +*span lite* can provide a `swap()`member function. See the table below and section [configuration](#configuration). + +### `operator==()` and other comparison functions + +*span lite* can provide functions to compare the content of two spans. However, C++20's span will not provide comparison and _span lite_ will omit comparison at default in the near future. See the table below and section [configuration](#configuration). See also [Revisiting Regular Types](#regtyp). + +### `same()` + +*span lite* can provide function `same()` to determine if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. See the table below and section [configuration](#configuration). + +### `first()`, `last()` and `subspan()` + +*span lite* can provide functions `first()`, `last()` and `subspan()` to avoid having to use the *dot template* syntax when the span is a dependent type. See the table below and section [configuration](#configuration). + +### `make_span()` + +*span lite* can provide `make_span()` creator functions to compensate for the class template argument deduction that is missing from pre-C++17 compilers. See the table below and section [configuration](#configuration). + +### `byte_span()` + +*span lite* can provide `byte_span()` creator functions to represent an object as a span of bytes. This requires the C++17 type `std::byte` to be available. See the table below and section [configuration](#configuration). + +| Kind | std | Function or method | +|--------------------|------|--------------------| +| **Macro** | | macro **`span_FEATURE_WITH_CONTAINER`**
macro **`span_FEATURE_WITH_CONTAINER_TO_STD`** | +| **Types** | | **with_container_t** type to disambiguate below constructors | +| **Objects** | | **with_container** value to disambiguate below constructors | +| **Constructors** | | macro **`span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE`**| +|   | | template<class Container>
constexpr **span**(with_container_t, Container & cont) | +|   | | template<class Container>
constexpr **span**(with_container_t, Container const & cont) | +|   | |   | +| **Methods** | | macro **`span_FEATURE_MEMBER_CALL_OPERATOR`** | +|   | | constexpr reference **operator()**(index_t idx) const
Equivalent to **operator[]**(), marked `[[deprecated]]` | +|   | |   | +| **Methods** | | macro **`span_FEATURE_MEMBER_AT`** | +|   | | constexpr reference **at**(index_t idx) const
May throw std::out_of_range exception | +|   | |   | +| **Methods** | | macro **`span_FEATURE_MEMBER_BACK_FRONT`** (on since v0.5.0) | +|   | | constexpr reference **back()** const noexcept | +|   | | constexpr reference **front()** const noexcept | +|   | |   | +| **Method** | | macro **`span_FEATURE_MEMBER_SWAP`** | +|   | | constexpr void **swap**(span & other) noexcept | +|   | |   | +| **Free functions** | | macro **`span_FEATURE_COMPARISON`** | +|

== != < > <= >= | | template<class T1, index_t E1, class T2, index_t E2>
constexpr bool
**operator==**( span const & l, span const & r) noexcept | +|   | |   | +| **Free function** | | macro **`span_FEATURE_SAME`** | +|   | | template<class T1, index_t E1, class T2, index_t E2>
constexpr bool
**same**( span const & l, span const & r) noexcept | +|   | |   | +| **Free functions** | | macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB`** | +|   | >= C++11 | template<extent_t Count, class T>
constexpr auto
**first**(T & t) ->... | +|   | >= C++11 | template<class T>
constexpr auto
**first**(T & t, index_t count) ->... | +|   | >= C++11 | template<extent_t Count, class T>
constexpr auto
**last**(T & t) ->... | +|   | >= C++11 | template<class T>
constexpr auto
**last**(T & t, extent_t count) ->... | +|   | >= C++11 | template<index_t Offset, extent_t Count = dynamic_extent, class T>
constexpr auto
**subspan**(T & t) ->... | +|   | >= C++11 | template<class T>
constexpr auto
**subspan**(T & t, index_t offset, extent_t count = dynamic_extent) ->... | +|   |   |   | +| **Free functions** | | macro **`span_FEATURE_MAKE_SPAN`**
macro **`span_FEATURE_MAKE_SPAN_TO_STD`** | +|   |   | template<class T>
constexpr span<T>
**make_span**(T \* first, T \* last) noexcept | +|   |   | template<class T>
constexpr span<T>
**make_span**(T \* ptr, index_t count) noexcept | +|   |   | template<class T, size_t N>
constexpr span<T,N>
**make_span**(T (&arr)[N]) noexcept | +|   | >= C++11 | template<class T, size_t N>
constexpr span<T,N>
**make_span**(std::array<T,N> & arr) noexcept | +|   | >= C++11 | template<class T, size_t N>
constexpr span<const T,N>
**make_span**(std::array<T,N > const & arr) noexcept | +|   | >= C++11 | template<class Container>
constexpr auto
**make_span**(Container & cont) ->
 span<typename Container::value_type> noexcept | +|   | >= C++11 | template<class Container>
constexpr auto
**make_span**(Container const & cont) ->
 span<const typename Container::value_type> noexcept | +|   |   | template<class Container>
span<typename Container::value_type>
**make_span**( with_container_t, Container & cont ) | +|   |   | template<class Container>
span<const typename Container::value_type>
**make_span**( with_container_t, Container const & cont ) | +|   | < C++11 | template<class T, Allocator>
span<T>
**make_span**(std::vector<T, Allocator> & cont) | +|   | < C++11 | template<class T, Allocator>
span<const T>
**make_span**(std::vector<T, Allocator> const & cont) | +|   |   |   | +| **Free functions** | | macro **`span_FEATURE_BYTE_SPAN`** | +|   | >= C++11 | template<class T>
span<T, sizeof(T)>
**byte_span**(T & t) | +|   | >= C++11 | template<class T>
span<const T, sizeof(T)>
**byte_span**(T const & t) | + +## Configuration + +### Tweak header + +If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *span lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/span.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define span_CONFIG_NO_EXCEPTIONS 1`. + +### Standard selection macro + +\-Dspan\_CPLUSPLUS=199711L +Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly. + +### Select `std::span` or `nonstd::span` + +At default, *span lite* uses `std::span` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::span` or span lite's `nonstd::span` as `nonstd::span` via the following macros. + +-Dspan\_CONFIG\_SELECT\_SPAN=span_SPAN_DEFAULT +Define this to `span_SPAN_STD` to select `std::span` as `nonstd::span`. Define this to `span_SPAN_NONSTD` to select `nonstd::span` as `nonstd::span`. Default is undefined, which has the same effect as defining to `span_SPAN_DEFAULT`. + +### Select extent type + +-Dspan_CONFIG_EXTENT_TYPE=std::size_t +Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). + +### Select size type + +-Dspan_CONFIG_SIZE_TYPE=std::size_t +Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). Note `span_CONFIG_SIZE_TYPE` replaces `span_CONFIG_INDEX_TYPE` which is deprecated. + +### Disable exceptions + +-Dspan_CONFIG_NO_EXCEPTIONS=0 +Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Disabling exceptions will force contract violation to use termination, see [contract violation macros](#contract-violation-response-macros). Default is undefined. + +### Provide construction using `with_container_t` + +-Dspan_FEATURE_WITH_CONTAINER=0 +Define this to 1 to enable constructing a span using `with_container_t`. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined. + +-Dspan_FEATURE_WITH_CONTAINER_TO_STD=*n* +Define this to the highest C++ language version for which to enable constructing a span using `with_container_t`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_WITH_CONTAINER` for this. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined. + +### Provide construction from `std::array` with const data + +-Dspan_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE=0 +Define this to 1 to enable constructing a span from a std::array with const data. Default is undefined. + +### Provide `operator()` member function + +-Dspan_FEATURE_MEMBER_CALL_OPERATOR=0 +Define this to 1 to provide member function `operator()`for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. Default is undefined. + +### Provide `at()` member function + +-Dspan_FEATURE_MEMBER_AT=0 +Define this to 1 to provide member function `at()`. Define this to 2 to include index and size in message of std::out_of_range exception. Default is undefined. + +### Provide `back()` and `front()` member functions + +-Dspan_FEATURE_MEMBER_BACK_FRONT=1 _(on since v0.5.0)_ +Define this to 0 to omit member functions `back()` and `front()`. Default is undefined. + +### Provide `swap()` member function + +-Dspan_FEATURE_MEMBER_SWAP=0 +Define this to 1 to provide member function `swap()`. Default is undefined. + +### Provide `operator==()` and other comparison functions + +-Dspan_FEATURE_COMPARISON=0 +Define this to 1 to include the comparison functions to compare the content of two spans. C++20's span does not provide comparison and _span lite_ omits comparison from v0.7.0. Default is undefined. + +### Provide `same()` function + +-Dspan_FEATURE_SAME=0 +Define this to 1 to provide function `same()` to test if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. Default is undefined. + +### Provide `first()`, `last()` and `subspan()` functions + +-Dspan_FEATURE_NON_MEMBER_FIRST_LAST_SUB=0 +Define this to 1 to provide functions `first()`, `last()` and `subspan()`. This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined. + +### Provide `make_span()` functions + +-Dspan_FEATURE_MAKE_SPAN=0 +Define this to 1 to provide creator functions `nonstd::make_span()`. This feature is implied by using `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1`. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined. + +-Dspan_FEATURE_MAKE_SPAN_TO_STD=*n* +Define this to the highest C++ language version for which to provide creator functions `nonstd::make_span()`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_MAKE_SPAN` for this. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined. + +### Provide `byte_span()` functions + +-Dspan_FEATURE_BYTE_SPAN=0 +Define this to 1 to provide creator functions `nonstd::byte_span()`. Default is undefined. + +### Contract violation response macros + +*span-lite* provides contract violation response control as suggested in proposal [N4415](http://wg21.link/n4415). + +\-Dspan\_CONFIG\_CONTRACT\_LEVEL\_ON (*default*) +Define this macro to include both `span_EXPECTS` and `span_ENSURES` in the code. This is the default case. + +\-Dspan\_CONFIG\_CONTRACT\_LEVEL\_OFF +Define this macro to exclude both `span_EXPECTS` and `span_ENSURES` from the code. + +\-Dspan\_CONFIG_CONTRACT\_LEVEL\_EXPECTS\_ONLY +Define this macro to include `span_EXPECTS` in the code and exclude `span_ENSURES` from the code. + +\-Dspan\_CONFIG\_CONTRACT\_LEVEL\_ENSURES\_ONLY +Define this macro to exclude `span_EXPECTS` from the code and include `span_ENSURES` in the code. + +\-Dspan\_CONFIG\_CONTRACT\_VIOLATION\_TERMINATES (*default*) +Define this macro to call `std::terminate()` on a contract violation in `span_EXPECTS`, `span_ENSURES`. This is the default case. + +\-Dspan\_CONFIG\_CONTRACT\_VIOLATION\_THROWS +Define this macro to throw an exception of implementation-defined type that is derived from `std::runtime_exception` instead of calling `std::terminate()` on a contract violation in `span_EXPECTS` and `span_ENSURES`. See also [disable exceptions](#disable-exceptions). + +Reported to work with +-------------------- +The table below mentions the compiler versions *span lite* is reported to work with. + +OS | Compiler | Where | Versions | +------------:|:-----------|:--------|:---------| +**GNU/Linux**| Clang/LLVM | Travis | 3.5.0, 3.6.2, 3.7.1, 3.8.0, 3.9.1, 4.0.1 | +   | GCC | Travis | 5.5.0, 6.4.0, 7.3.0 | +**OS X** | ? | Local | ? | +**Windows** | Clang/LLVM | Local | 6.0.0 | +  | GCC | Local | 7.2.0 | +  | Visual C++
(Visual Studio)| Local | 8 (2005), 10 (2010), 11 (2012),
12 (2013), 14 (2015), 15 (2017) | +  | Visual C++
(Visual Studio)| AppVeyor | 10 (2010), 11 (2012),
12 (2013), 14 (2015), 15 (2017) | + +## Building the tests + +To build the tests you need: + +- [CMake](http://cmake.org), version 3.0 or later to be installed and in your PATH. +- A [suitable compiler](#reported-to-work-with). + +The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). + +The following steps assume that the [*span lite* source code](https://github.com/martinmoene/span-lite) has been cloned into a directory named `./span-lite`. + +1. Create a directory for the build outputs. + + cd ./span-lite + md build && cd build + +2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). + + cmake -G "Unix Makefiles" -DSPAN_LITE_OPT_BUILD_TESTS=ON .. + +3. Optional. You can control above configuration through the following options: + + `-DSPAN_LITE_OPT_BUILD_TESTS=ON`: build the tests for span, default off + `-DSPAN_LITE_OPT_BUILD_EXAMPLES=OFF`: build the examples, default off + +4. Build the test suite. + + cmake --build . + +5. Run the test suite. + + ctest -V + +All tests should pass, indicating your platform is supported and you are ready to use *span lite*. + +## Other implementations of span + +- *gsl-lite* [span](https://github.com/martinmoene/gsl-lite/blob/73c4f16f2b35fc174fc2f09d44d5ab13e5c638c3/include/gsl/gsl-lite.hpp#L1221). +- Microsoft GSL [span](https://github.com/Microsoft/GSL/blob/master/include/gsl/span). +- Google Abseil [span](https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h). +- Marshall Clow's [libc++ span snippet](https://github.com/mclow/snippets/blob/master/span.cpp). +- Tristan Brindle's [Implementation of C++20's std::span for older compilers](https://github.com/tcbrindle/span). +- [Search _span c++_ on GitHub](https://github.com/search?l=C%2B%2B&q=span+c%2B%2B&type=Repositories&utf8=%E2%9C%93). + +## Notes and references + +*Interface and specification* + +- [span on cppreference](https://en.cppreference.com/w/cpp/container/span). +- [p0122 - C++20 Proposal](http://wg21.link/p0122). +- [span in C++20 Working Draft](http://eel.is/c++draft/views). + +*Presentations* + +- TBD + +*Proposals* + +- [p0122 - span: bounds-safe views for sequences of objects](http://wg21.link/p0122). +- [p1024 - Usability Enhancements for std::span](http://wg21.link/p1024). +- [p1419 - A SFINAE-friendly trait to determine the extent of statically sized containers](http://wg21.link/p1419). +- [p0805 - Comparing Containers](http://wg21.link/p0805). +- [p1085 - Should Span be Regular?](http://wg21.link/p0805). +- [p0091 - Template argument deduction for class templates](http://wg21.link/p0091). +- [p0856 - Restrict Access Property for mdspan and span](http://wg21.link/p0856). +- [p1428 - Subscripts and sizes should be signed](http://wg21.link/p1428). +- [p1089 - Sizes Should Only span Unsigned](http://wg21.link/p1089). +- [p1227 - Signed size() functions](http://wg21.link/p1227). +- [p1872 - span should have size_type, not index_type](http://wg21.link/p1872). +- [lwg 3101 - span's Container constructors need another constraint](https://cplusplus.github.io/LWG/issue3101). +- [Reddit - 2018-06 Rapperswil ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/8prqzm/2018_rapperswil_iso_c_committee_trip_report/) +- [Reddit - 2018-11 San Diego ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/). +- [Reddit - 2019-02 Kona ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/au0c4x/201902_kona_iso_c_committee_trip_report_c20/). +- [Reddit - 2019-07 Cologne ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/cfk9de/201907_cologne_iso_c_committee_trip_report_the/) +- [Reddit - 2019-11 Belfast ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/dtuov8/201911_belfast_iso_c_committee_trip_report/) +- Titus Winters. [Revisiting Regular Types](https://abseil.io/blog/20180531-regular-types). Abseil Blog. 31 May 2018. + +## Appendix + +### A.1 Compile-time information + +The version of *span lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. + +### A.2 Span lite test specification + +
+click to expand +

+ +```Text +span<>: Terminates construction from a nullptr and a non-zero size (C++11) +span<>: Terminates construction from two pointers in the wrong order +span<>: Terminates construction from a null pointer and a non-zero size +span<>: Terminates creation of a sub span of the first n elements for n exceeding the span +span<>: Terminates creation of a sub span of the last n elements for n exceeding the span +span<>: Terminates creation of a sub span outside the span +span<>: Terminates access outside the span +span<>: Throws on access outside the span via at(): std::out_of_range [span_FEATURE_MEMBER_AT>0][span_CONFIG_NO_EXCEPTIONS=0] +span<>: Termination throws std::logic_error-derived exception [span_CONFIG_CONTRACT_VIOLATION_THROWS=1] +span<>: Allows to default-construct +span<>: Allows to construct from a nullptr and a zero size (C++11) +span<>: Allows to construct from two pointers +span<>: Allows to construct from two iterators +span<>: Allows to construct from two iterators - empty range +span<>: Allows to construct from an iterator and a size +span<>: Allows to construct from an iterator and a size - empty range +span<>: Allows to construct from two pointers to const +span<>: Allows to construct from a non-null pointer and a size +span<>: Allows to construct from a non-null pointer to const and a size +span<>: Allows to construct from a temporary pointer and a size +span<>: Allows to construct from a temporary pointer to const and a size +span<>: Allows to construct from any pointer and a zero size (C++98) +span<>: Allows to construct from a pointer and a size via a deduction guide (C++17) +span<>: Allows to construct from an iterator and a size via a deduction guide (C++17) +span<>: Allows to construct from two iterators via a deduction guide (C++17) +span<>: Allows to construct from a C-array +span<>: Allows to construct from a C-array via a deduction guide (C++17) +span<>: Allows to construct from a const C-array +span<>: Allows to construct from a C-array with size via decay to pointer (potentially dangerous) +span<>: Allows to construct from a const C-array with size via decay to pointer (potentially dangerous) +span<>: Allows to construct from a std::initializer_list<> (C++11) +span<>: Allows to construct from a std::array<> (C++11) +span<>: Allows to construct from a std::array via a deduction guide (C++17) +span<>: Allows to construct from a std::array<> with const data (C++11, span_FEATURE_CONSTR..._ELEMENT_TYPE=1) +span<>: Allows to construct from an empty std::array<> (C++11) +span<>: Allows to construct from a container (std::vector<>) +span<>: Allows to construct from a container via a deduction guide (std::vector<>, C++17) +span<>: Allows to tag-construct from a container (std::vector<>) +span<>: Allows to tag-construct from a const container (std::vector<>) +span<>: Allows to copy-construct from another span of the same type +span<>: Allows to copy-construct from another span of a compatible type +span<>: Allows to copy-construct from a temporary span of the same type (C++11) +span<>: Allows to copy-assign from another span of the same type +span<>: Allows to copy-assign from a temporary span of the same type (C++11) +span<>: Allows to create a sub span of the first n elements +span<>: Allows to create a sub span of the last n elements +span<>: Allows to create a sub span starting at a given offset +span<>: Allows to create a sub span starting at a given offset with a given length +span<>: Allows to observe an element via array indexing +span<>: Allows to observe an element via call indexing +span<>: Allows to observe an element via at() [span_FEATURE_MEMBER_AT>0] +span<>: Allows to observe an element via data() +span<>: Allows to observe the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to observe the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to change an element via array indexing +span<>: Allows to change an element via call indexing +span<>: Allows to change an element via at() [span_FEATURE_MEMBER_AT>0] +span<>: Allows to change an element via data() +span<>: Allows to change the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to change the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to swap with another span [span_FEATURE_MEMBER_SWAP=1] +span<>: Allows forward iteration +span<>: Allows const forward iteration +span<>: Allows reverse iteration +span<>: Allows const reverse iteration +span<>: Allows to identify if a span is the same as another span [span_FEATURE_SAME=1] +span<>: Allows to compare equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare unequal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare less than another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare less than or equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare greater than another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare greater than or equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare to another span of the same type and different cv-ness [span_FEATURE_SAME=0] +span<>: Allows to compare empty spans as equal [span_FEATURE_COMPARISON=1] +span<>: Allows to test for empty span via empty(), empty case +span<>: Allows to test for empty span via empty(), non-empty case +span<>: Allows to obtain the number of elements via size() +span<>: Allows to obtain the number of elements via ssize() +span<>: Allows to obtain the number of bytes via size_bytes() +span<>: Allows to view the elements as read-only bytes +span<>: Allows to view and change the elements as writable bytes +make_span() [span_FEATURE_MAKE_SPAN_TO_STD=99] +make_span(): Allows building from two pointers +make_span(): Allows building from two const pointers +make_span(): Allows building from a non-null pointer and a size +make_span(): Allows building from a non-null const pointer and a size +make_span(): Allows building from a C-array +make_span(): Allows building from a const C-array +make_span(): Allows building from a std::initializer_list<> (C++11) +make_span(): Allows building from a std::array<> (C++11) +make_span(): Allows building from a const std::array<> (C++11) +make_span(): Allows building from a container (std::vector<>) +make_span(): Allows building from a const container (std::vector<>) +make_span(): Allows building from a container (with_container_t, std::vector<>) +make_span(): Allows building from a const container (with_container_t, std::vector<>) +byte_span() [span_FEATURE_BYTE_SPAN=1] +byte_span(): Allows building a span of std::byte from a single object (C++17, byte-lite) +byte_span(): Allows building a span of const std::byte from a single const object (C++17, byte-lite) +first(), last(), subspan() [span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1] +first(): Allows to create a sub span of the first n elements +last(): Allows to create a sub span of the last n elements +subspan(): Allows to create a sub span starting at a given offset +size(): Allows to obtain the number of elements via size() +ssize(): Allows to obtain the number of elements via ssize() +tuple_size<>: Allows to obtain the number of elements via std::tuple_size<> (C++11) +tuple_element<>: Allows to obtain an element via std::tuple_element<> (C++11) +tuple_element<>: Allows to obtain an element via std::tuple_element_t<> (C++11) +get(spn): Allows to access an element via std::get<>() +tweak header: reads tweak header if supported [tweak] +``` + +

+
diff --git a/dep/span-lite/include/span.hpp b/dep/span-lite/include/span.hpp new file mode 100644 index 000000000..8ec88e0ad --- /dev/null +++ b/dep/span-lite/include/span.hpp @@ -0,0 +1,1817 @@ +// +// span for C++98 and later. +// Based on http://wg21.link/p0122r7 +// For more information see https://github.com/martinmoene/span-lite +// +// Copyright 2018-2020 Martin Moene +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef NONSTD_SPAN_HPP_INCLUDED +#define NONSTD_SPAN_HPP_INCLUDED + +#define span_lite_MAJOR 0 +#define span_lite_MINOR 9 +#define span_lite_PATCH 2 + +#define span_lite_VERSION span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH) + +#define span_STRINGIFY( x ) span_STRINGIFY_( x ) +#define span_STRINGIFY_( x ) #x + +// span configuration: + +#define span_SPAN_DEFAULT 0 +#define span_SPAN_NONSTD 1 +#define span_SPAN_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include() +# include +# endif +#define span_HAVE_TWEAK_HEADER 1 +#else +#define span_HAVE_TWEAK_HEADER 0 +//# pragma message("span.hpp: Note: Tweak header not supported.") +#endif + +// span selection and configuration: + +#define span_HAVE( feature ) ( span_HAVE_##feature ) + +#ifndef span_CONFIG_SELECT_SPAN +# define span_CONFIG_SELECT_SPAN ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD ) +#endif + +#ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +#endif + +#ifndef span_CONFIG_SIZE_TYPE +# define span_CONFIG_SIZE_TYPE std::size_t +#endif + +#ifdef span_CONFIG_INDEX_TYPE +# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`. +#endif + +// span configuration (features): + +#ifndef span_FEATURE_WITH_CONTAINER +#ifdef span_FEATURE_WITH_CONTAINER_TO_STD +# define span_FEATURE_WITH_CONTAINER span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD ) +#else +# define span_FEATURE_WITH_CONTAINER 0 +#endif +#endif + +#ifndef span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE +# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE 0 +#endif + +#ifndef span_FEATURE_MEMBER_AT +# define span_FEATURE_MEMBER_AT 0 +#endif + +#ifndef span_FEATURE_MEMBER_BACK_FRONT +# define span_FEATURE_MEMBER_BACK_FRONT 1 +#endif + +#ifndef span_FEATURE_MEMBER_CALL_OPERATOR +# define span_FEATURE_MEMBER_CALL_OPERATOR 0 +#endif + +#ifndef span_FEATURE_MEMBER_SWAP +# define span_FEATURE_MEMBER_SWAP 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB 0 +#endif + +#ifndef span_FEATURE_COMPARISON +# define span_FEATURE_COMPARISON 0 // Note: C++20 does not provide comparison +#endif + +#ifndef span_FEATURE_SAME +# define span_FEATURE_SAME 0 +#endif + +#if span_FEATURE_SAME && !span_FEATURE_COMPARISON +# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON` +#endif + +#ifndef span_FEATURE_MAKE_SPAN +#ifdef span_FEATURE_MAKE_SPAN_TO_STD +# define span_FEATURE_MAKE_SPAN span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD ) +#else +# define span_FEATURE_MAKE_SPAN 0 +#endif +#endif + +#ifndef span_FEATURE_BYTE_SPAN +# define span_FEATURE_BYTE_SPAN 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef span_CONFIG_NO_EXCEPTIONS +# if _MSC_VER +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define span_CONFIG_NO_EXCEPTIONS 0 +# else +# define span_CONFIG_NO_EXCEPTIONS 1 +# undef span_CONFIG_CONTRACT_VIOLATION_THROWS +# undef span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# define span_CONFIG_CONTRACT_VIOLATION_THROWS 0 +# define span_CONFIG_CONTRACT_VIOLATION_TERMINATES 1 +# endif +#endif + +// Control pre- and postcondition violation behaviour: + +#if defined( span_CONFIG_CONTRACT_LEVEL_ON ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#elif defined( span_CONFIG_CONTRACT_LEVEL_OFF ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x00 +#elif defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x01 +#elif defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x10 +#else +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V span_CONFIG_CONTRACT_VIOLATION_THROWS +#else +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \ + defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both. +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef span_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define span_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define span_CPLUSPLUS __cplusplus +# endif +#endif + +#define span_CPP98_OR_GREATER ( span_CPLUSPLUS >= 199711L ) +#define span_CPP11_OR_GREATER ( span_CPLUSPLUS >= 201103L ) +#define span_CPP14_OR_GREATER ( span_CPLUSPLUS >= 201402L ) +#define span_CPP17_OR_GREATER ( span_CPLUSPLUS >= 201703L ) +#define span_CPP20_OR_GREATER ( span_CPLUSPLUS >= 202000L ) + +// C++ language version (represent 98 as 3): + +#define span_CPLUSPLUS_V ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +#define span_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V ) + +#define span_CONFIG( feature ) ( span_CONFIG_##feature ) +#define span_FEATURE( feature ) ( span_FEATURE_##feature ) +#define span_FEATURE_TO_STD( feature ) ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) ) + +// Use C++20 std::span if available and requested: + +#if span_CPP20_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define span_HAVE_STD_SPAN 1 +# else +# define span_HAVE_STD_SPAN 0 +# endif +#else +# define span_HAVE_STD_SPAN 0 +#endif + +#define span_USES_STD_SPAN ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) ) + +// +// Use C++20 std::span: +// + +#if span_USES_STD_SPAN + +#include + +namespace nonstd { + +using std::span; + +// Note: C++20 does not provide comparison +// using std::operator==; +// using std::operator!=; +// using std::operator<; +// using std::operator<=; +// using std::operator>; +// using std::operator>=; +} // namespace nonstd + +#else // span_USES_STD_SPAN + +#include + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 span_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 span_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 span_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 span_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 span_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 span_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 span_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 span_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 span_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 span_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 span_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define span_COMPILER_MSVC_VER (_MSC_VER ) +# define span_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define span_COMPILER_MSVC_VER 0 +# define span_COMPILER_MSVC_VERSION 0 +#endif + +#define span_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined(__clang__) +# define span_COMPILER_CLANG_VERSION span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define span_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define span_COMPILER_GNUC_VERSION span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define span_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define span_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler warning suppression: + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wmismatched-tags" +# define span_RESTORE_WARNINGS() _Pragma( "clang diagnostic pop" ) + +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +# define span_RESTORE_WARNINGS() _Pragma( "GCC diagnostic pop" ) + +#elif span_COMPILER_MSVC_VER >= 1900 +# define span_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +# define span_RESTORE_WARNINGS() __pragma(warning(pop )) + +// Suppress the following MSVC GSL warnings: +// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' +// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narrow +// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead +// - C26490: gsl::t.1 : don't use reinterpret_cast + +span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 ) + +#else +# define span_RESTORE_WARNINGS() /*empty*/ +#endif + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define span_HAS_CPP0X _HAS_CPP0X +#else +# define span_HAS_CPP0X 0 +#endif + +#define span_CPP11_80 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400) +#define span_CPP11_90 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500) +#define span_CPP11_100 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600) +#define span_CPP11_110 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700) +#define span_CPP11_120 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP11_140 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP14_000 (span_CPP14_OR_GREATER) +#define span_CPP14_120 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP14_140 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP17_000 (span_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define span_HAVE_ALIAS_TEMPLATE span_CPP11_140 +#define span_HAVE_AUTO span_CPP11_100 +#define span_HAVE_CONSTEXPR_11 span_CPP11_140 +#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG span_CPP11_120 +#define span_HAVE_EXPLICIT_CONVERSION span_CPP11_140 +#define span_HAVE_INITIALIZER_LIST span_CPP11_120 +#define span_HAVE_IS_DEFAULT span_CPP11_140 +#define span_HAVE_IS_DELETE span_CPP11_140 +#define span_HAVE_NOEXCEPT span_CPP11_140 +#define span_HAVE_NULLPTR span_CPP11_100 +#define span_HAVE_STATIC_ASSERT span_CPP11_100 + +// Presence of C++14 language features: + +#define span_HAVE_CONSTEXPR_14 span_CPP14_000 + +// Presence of C++17 language features: + +#define span_HAVE_DEPRECATED span_CPP17_000 +#define span_HAVE_NODISCARD span_CPP17_000 +#define span_HAVE_NORETURN span_CPP17_000 + +// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 + +#if defined(__cpp_deduction_guides) +# define span_HAVE_DEDUCTION_GUIDES 1 +#else +# define span_HAVE_DEDUCTION_GUIDES (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 )) +#endif + +// Presence of C++ library features: + +#define span_HAVE_ADDRESSOF span_CPP17_000 +#define span_HAVE_ARRAY span_CPP11_110 +#define span_HAVE_BYTE span_CPP17_000 +#define span_HAVE_CONDITIONAL span_CPP11_120 +#define span_HAVE_CONTAINER_DATA_METHOD (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X )) +#define span_HAVE_DATA span_CPP17_000 +#define span_HAVE_LONGLONG span_CPP11_80 +#define span_HAVE_REMOVE_CONST span_CPP11_110 +#define span_HAVE_SNPRINTF span_CPP11_140 +#define span_HAVE_STRUCT_BINDING span_CPP11_120 +#define span_HAVE_TYPE_TRAITS span_CPP11_90 + +// Presence of byte-lite: + +#ifdef NONSTD_BYTE_LITE_HPP +# define span_HAVE_NONSTD_BYTE 1 +#else +# define span_HAVE_NONSTD_BYTE 0 +#endif + +// C++ feature usage: + +#if span_HAVE_ADDRESSOF +# define span_ADDRESSOF(x) std::addressof(x) +#else +# define span_ADDRESSOF(x) (&x) +#endif + +#if span_HAVE_CONSTEXPR_11 +# define span_constexpr constexpr +#else +# define span_constexpr /*span_constexpr*/ +#endif + +#if span_HAVE_CONSTEXPR_14 +# define span_constexpr14 constexpr +#else +# define span_constexpr14 /*span_constexpr*/ +#endif + +#if span_HAVE_EXPLICIT_CONVERSION +# define span_explicit explicit +#else +# define span_explicit /*explicit*/ +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete = delete +#else +# define span_is_delete +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete_access public +#else +# define span_is_delete_access private +#endif + +#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V +# define span_noexcept noexcept +#else +# define span_noexcept /*noexcept*/ +#endif + +#if span_HAVE_NULLPTR +# define span_nullptr nullptr +#else +# define span_nullptr NULL +#endif + +#if span_HAVE_DEPRECATED +# define span_deprecated(msg) [[deprecated(msg)]] +#else +# define span_deprecated(msg) /*[[deprecated]]*/ +#endif + +#if span_HAVE_NODISCARD +# define span_nodiscard [[nodiscard]] +#else +# define span_nodiscard /*[[nodiscard]]*/ +#endif + +#if span_HAVE_NORETURN +# define span_noreturn [[noreturn]] +#else +# define span_noreturn /*[[noreturn]]*/ +#endif + +// Other features: + +#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG +#define span_HAVE_ITERATOR_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG + +// Additional includes: + +#if span_HAVE( ADDRESSOF ) +# include +#endif + +#if span_HAVE( ARRAY ) +# include +#endif + +#if span_HAVE( BYTE ) +# include +#endif + +#if span_HAVE( DATA ) +# include // for std::data(), std::size() +#endif + +#if span_HAVE( TYPE_TRAITS ) +# include +#endif + +#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) +# include +#endif + +#if span_FEATURE( MEMBER_AT ) > 1 +# include +#endif + +#if ! span_CONFIG( NO_EXCEPTIONS ) +# include +#endif + +// Contract violation + +#define span_ELIDE_CONTRACT_EXPECTS ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) ) +#define span_ELIDE_CONTRACT_ENSURES ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) ) + +#if span_ELIDE_CONTRACT_EXPECTS +# define span_constexpr_exp span_constexpr +# define span_EXPECTS( cond ) /* Expect elided */ +#else +# define span_constexpr_exp span_constexpr14 +# define span_EXPECTS( cond ) span_CONTRACT_CHECK( "Precondition", cond ) +#endif + +#if span_ELIDE_CONTRACT_ENSURES +# define span_constexpr_ens span_constexpr +# define span_ENSURES( cond ) /* Ensures elided */ +#else +# define span_constexpr_ens span_constexpr14 +# define span_ENSURES( cond ) span_CONTRACT_CHECK( "Postcondition", cond ) +#endif + +#define span_CONTRACT_CHECK( type, cond ) \ + cond ? static_cast< void >( 0 ) \ + : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." ) + +#ifdef __GNUG__ +# define span_LOCATION( file, line ) file ":" span_STRINGIFY( line ) +#else +# define span_LOCATION( file, line ) file "(" span_STRINGIFY( line ) ")" +#endif + +// Method enabling + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +#define span_REQUIRES_0(VA) \ + template< bool B = (VA), typename std::enable_if::type = 0 > + +# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 ) +// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments +# define span_REQUIRES_T(VA) \ + , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type +# else +# define span_REQUIRES_T(VA) \ + , typename std::enable_if< (VA), int >::type = 0 +# endif + +#define span_REQUIRES_R(R, VA) \ + typename std::enable_if< (VA), R>::type + +#define span_REQUIRES_A(VA) \ + , typename std::enable_if< (VA), void*>::type = nullptr + +#else + +# define span_REQUIRES_0(VA) /*empty*/ +# define span_REQUIRES_T(VA) /*empty*/ +# define span_REQUIRES_R(R, VA) R +# define span_REQUIRES_A(VA) /*empty*/ + +#endif + +namespace nonstd { +namespace span_lite { + +// [views.constants], constants + +typedef span_CONFIG_EXTENT_TYPE extent_t; +typedef span_CONFIG_SIZE_TYPE size_t; + +span_constexpr const extent_t dynamic_extent = static_cast( -1 ); + +template< class T, extent_t Extent = dynamic_extent > +class span; + +// Tag to select span constructor taking a container (prevent ms-gsl warning C26426): + +struct with_container_t { span_constexpr with_container_t() span_noexcept {} }; +const span_constexpr with_container_t with_container; + +// C++11 emulation: + +namespace std11 { + +#if span_HAVE( REMOVE_CONST ) + +using std::remove_cv; +using std::remove_const; +using std::remove_volatile; + +#else + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const< T const > { typedef T type; }; + +template< class T > struct remove_volatile { typedef T type; }; +template< class T > struct remove_volatile< T volatile > { typedef T type; }; + +template< class T > +struct remove_cv +{ + typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type; +}; + +#endif // span_HAVE( REMOVE_CONST ) + +#if span_HAVE( TYPE_TRAITS ) + +using std::is_same; +using std::is_signed; +using std::integral_constant; +using std::true_type; +using std::false_type; +using std::remove_reference; + +#else + +template< class T, T v > struct integral_constant { enum { value = v }; }; +typedef integral_constant< bool, true > true_type; +typedef integral_constant< bool, false > false_type; + +template< class T, class U > struct is_same : false_type{}; +template< class T > struct is_same : true_type{}; + +template< typename T > struct is_signed : false_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; + +#endif + +} // namespace std11 + +// C++17 emulation: + +namespace std17 { + +template< bool v > struct bool_constant : std11::integral_constant{}; + +#if span_CPP11_120 + +template< class...> +using void_t = void; + +#endif + +#if span_HAVE( DATA ) + +using std::data; +using std::size; + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< typename T, std::size_t N > +inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t +{ + return N; +} + +template< typename C > +inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() ) +{ + return cont.size(); +} + +template< typename T, std::size_t N > +inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T* +{ + return &arr[0]; +} + +template< typename C > +inline span_constexpr auto data( C & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename C > +inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename E > +inline span_constexpr auto data( std::initializer_list il ) span_noexcept -> E const * +{ + return il.begin(); +} + +#endif // span_HAVE( DATA ) + +#if span_HAVE( BYTE ) +using std::byte; +#elif span_HAVE( NONSTD_BYTE ) +using nonstd::byte; +#endif + +} // namespace std17 + +// C++20 emulation: + +namespace std20 { + +#if span_HAVE( DEDUCTION_GUIDES ) +template< class T > +using iter_reference_t = decltype( *std::declval() ); +#endif + +} // namespace std20 + +// Implementation details: + +namespace detail { + +/*enum*/ struct enabler{}; + +template< typename T > +bool is_positive( T x ) +{ + return std11::is_signed::value ? x >= 0 : true; +} + +#if span_HAVE( TYPE_TRAITS ) + +template< class Q > +struct is_span_oracle : std::false_type{}; + +template< class T, span_CONFIG_EXTENT_TYPE Extent > +struct is_span_oracle< span > : std::true_type{}; + +template< class Q > +struct is_span : is_span_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_std_array_oracle : std::false_type{}; + +#if span_HAVE( ARRAY ) + +template< class T, std::size_t Extent > +struct is_std_array_oracle< std::array > : std::true_type{}; + +#endif + +template< class Q > +struct is_std_array : is_std_array_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_array : std::false_type {}; + +template< class T > +struct is_array : std::true_type {}; + +template< class T, std::size_t N > +struct is_array : std::true_type {}; + +#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 ) + +template< class, class = void > +struct has_size_and_data : std::false_type{}; + +template< class C > +struct has_size_and_data +< + C, std17::void_t< + decltype( std17::size(std::declval()) ), + decltype( std17::data(std::declval()) ) > +> : std::true_type{}; + +template< class, class, class = void > +struct is_compatible_element : std::false_type {}; + +template< class C, class E > +struct is_compatible_element +< + C, E, std17::void_t< + decltype( std17::data(std::declval()) ) > +> : std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >{}; + +template< class C > +struct is_container : std17::bool_constant +< + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && has_size_and_data< C >::value +>{}; + +template< class C, class E > +struct is_compatible_container : std17::bool_constant +< + is_container::value + && is_compatible_element::value +>{}; + +#else // span_CPP11_140 + +template< + class C, class E + span_REQUIRES_T(( + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && ( std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >::value) + // && has_size_and_data< C >::value + )) + , class = decltype( std17::size(std::declval()) ) + , class = decltype( std17::data(std::declval()) ) +> +struct is_compatible_container : std::true_type{}; + +#endif // span_CPP11_140 + +#endif // span_HAVE( TYPE_TRAITS ) + +#if ! span_CONFIG( NO_EXCEPTIONS ) +#if span_FEATURE( MEMBER_AT ) > 1 + +// format index and size: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wlong-long" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wformat=ll" +# pragma GCC diagnostic ignored "-Wlong-long" +#endif + +inline void throw_out_of_range( size_t idx, size_t size ) +{ + const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)"; + char buffer[ 2 * 20 + sizeof fmt ]; + sprintf( buffer, fmt, static_cast(idx), static_cast(size) ); + + throw std::out_of_range( buffer ); +} + +#else // MEMBER_AT + +inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ ) +{ + throw std::out_of_range( "span::at(): index outside span" ); +} +#endif // MEMBER_AT +#endif // NO_EXCEPTIONS + +#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +struct contract_violation : std::logic_error +{ + explicit contract_violation( char const * const message ) + : std::logic_error( message ) + {} +}; + +inline void report_contract_violation( char const * msg ) +{ + throw contract_violation( msg ); +} + +#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept +{ + std::terminate(); +} + +#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +} // namespace detail + +// Prevent signed-unsigned mismatch: + +#define span_sizeof(T) static_cast( sizeof(T) ) + +template< class T > +inline span_constexpr size_t to_size( T size ) +{ + return static_cast( size ); +} + +// +// [views.span] - A view over a contiguous, single-dimension sequence of objects +// +template< class T, extent_t Extent /*= dynamic_extent*/ > +class span +{ +public: + // constants and types + + typedef T element_type; + typedef typename std11::remove_cv< T >::type value_type; + + typedef T & reference; + typedef T * pointer; + typedef T const * const_pointer; + typedef T const & const_reference; + + typedef size_t size_type; + typedef extent_t extent_type; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef std::ptrdiff_t difference_type; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + +// static constexpr extent_type extent = Extent; + enum { extent = Extent }; + + // 26.7.3.2 Constructors, copy, and assignment [span.cons] + + span_REQUIRES_0( + ( Extent == 0 ) || + ( Extent == dynamic_extent ) + ) + span_constexpr span() span_noexcept + : data_( span_nullptr ) + , size_( 0 ) + { + // span_EXPECTS( data() == span_nullptr ); + // span_EXPECTS( size() == 0 ); + } + +#if span_HAVE( ITERATOR_CTOR ) + // Didn't yet succeed in combining the next two constructors: + + span_constexpr_exp span( std::nullptr_t, size_type count ) + : data_( span_nullptr ) + , size_( count ) + { + span_EXPECTS( data_ == span_nullptr && count == 0 ); + } + + template< typename It + span_REQUIRES_T(( + std::is_convertible()), element_type>::value + )) + > + span_constexpr_exp span( It first, size_type count ) + : data_( to_address( first ) ) + , size_( count ) + { + span_EXPECTS( + ( data_ == span_nullptr && count == 0 ) || + ( data_ != span_nullptr && detail::is_positive( count ) ) + ); + } +#else + span_constexpr_exp span( pointer ptr, size_type count ) + : data_( ptr ) + , size_( count ) + { + span_EXPECTS( + ( ptr == span_nullptr && count == 0 ) || + ( ptr != span_nullptr && detail::is_positive( count ) ) + ); + } +#endif + +#if span_HAVE( ITERATOR_CTOR ) + template< typename It, typename End + span_REQUIRES_T(( + std::is_convertible()), element_type>::value + && ! std::is_convertible::value + )) + > + span_constexpr_exp span( It first, End last ) + : data_( to_address( first ) ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#else + span_constexpr_exp span( pointer first, pointer last ) + : data_( first ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#endif + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > + span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept + : data_( span_ADDRESSOF( arr[0] ) ) + , size_( N ) + {} + +#if span_HAVE( ARRAY ) + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > +# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE ) + span_constexpr span( std::array< element_type, N > & arr ) span_noexcept +# else + span_constexpr span( std::array< value_type, N > & arr ) span_noexcept +# endif + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + + template< std::size_t N +# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) +# endif + > + span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + +#endif // span_HAVE( ARRAY ) + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + template< class Container + span_REQUIRES_T(( + detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + + template< class Container + span_REQUIRES_T(( + std::is_const< element_type >::value + && detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container const & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + +#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +#if span_FEATURE( WITH_CONTAINER ) + + template< class Container > + span_constexpr span( with_container_t, Container & cont ) + : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) ) + , size_( to_size( cont.size() ) ) + {} + + template< class Container > + span_constexpr span( with_container_t, Container const & cont ) + : data_( cont.size() == 0 ? span_nullptr : const_cast( span_ADDRESSOF( cont[0] ) ) ) + , size_( to_size( cont.size() ) ) + {} +#endif + +#if span_HAVE( IS_DEFAULT ) + span_constexpr span( span const & other ) span_noexcept = default; + + ~span() span_noexcept = default; + + span_constexpr14 span & operator=( span const & other ) span_noexcept = default; +#else + span_constexpr span( span const & other ) span_noexcept + : data_( other.data_ ) + , size_( other.size_ ) + {} + + ~span() span_noexcept + {} + + span_constexpr14 span & operator=( span const & other ) span_noexcept + { + data_ = other.data_; + size_ = other.size_; + + return *this; + } +#endif + + template< class OtherElementType, extent_type OtherExtent + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == OtherExtent) + && std::is_convertible::value + )) + > + span_constexpr_exp span( span const & other ) span_noexcept + : data_( reinterpret_cast( other.data() ) ) + , size_( other.size() ) + { + span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) ); + } + + // 26.7.3.3 Subviews [span.sub] + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + first() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data(), Count ); + } + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + last() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data() + (size() - Count), Count ); + } + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + template< size_type Offset, extent_type Count = dynamic_extent > +#else + template< size_type Offset, extent_type Count /*= dynamic_extent*/ > +#endif + span_constexpr_exp span< element_type, Count > + subspan() const + { + span_EXPECTS( + ( detail::is_positive( Offset ) && Offset <= size() ) && + ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) ) + ); + + return span< element_type, Count >( + data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + first( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data(), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + last( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data() + ( size() - count ), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + subspan( size_type offset, size_type count = static_cast(dynamic_extent) ) const + { + span_EXPECTS( + ( ( detail::is_positive( offset ) && offset <= size() ) ) && + ( count == static_cast(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) ) + ); + + return span< element_type, dynamic_extent >( + data() + offset, count == static_cast(dynamic_extent) ? size() - offset : count ); + } + + // 26.7.3.4 Observers [span.obs] + + span_constexpr size_type size() const span_noexcept + { + return size_; + } + + span_constexpr std::ptrdiff_t ssize() const span_noexcept + { + return static_cast( size_ ); + } + + span_constexpr size_type size_bytes() const span_noexcept + { + return size() * to_size( sizeof( element_type ) ); + } + + span_nodiscard span_constexpr bool empty() const span_noexcept + { + return size() == 0; + } + + // 26.7.3.5 Element access [span.elem] + + span_constexpr_exp reference operator[]( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } + +#if span_FEATURE( MEMBER_CALL_OPERATOR ) + span_deprecated("replace operator() with operator[]") + + span_constexpr_exp reference operator()( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } +#endif + +#if span_FEATURE( MEMBER_AT ) + span_constexpr14 reference at( size_type idx ) const + { +#if span_CONFIG( NO_EXCEPTIONS ) + return this->operator[]( idx ); +#else + if ( !detail::is_positive( idx ) || size() <= idx ) + { + detail::throw_out_of_range( idx, size() ); + } + return *( data() + idx ); +#endif + } +#endif + + span_constexpr pointer data() const span_noexcept + { + return data_; + } + +#if span_FEATURE( MEMBER_BACK_FRONT ) + + span_constexpr_exp reference front() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *data(); + } + + span_constexpr_exp reference back() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *( data() + size() - 1 ); + } + +#endif + + // xx.x.x.x Modifiers [span.modifiers] + +#if span_FEATURE( MEMBER_SWAP ) + + span_constexpr14 void swap( span & other ) span_noexcept + { + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } +#endif + + // 26.7.3.6 Iterator support [span.iterators] + + span_constexpr iterator begin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return iterator( data() ); +#endif + } + + span_constexpr iterator end() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return iterator( data() + size() ); +#endif + } + + span_constexpr const_iterator cbegin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return const_iterator( data() ); +#endif + } + + span_constexpr const_iterator cend() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return const_iterator( data() + size() ); +#endif + } + + span_constexpr reverse_iterator rbegin() const span_noexcept + { + return reverse_iterator( end() ); + } + + span_constexpr reverse_iterator rend() const span_noexcept + { + return reverse_iterator( begin() ); + } + + span_constexpr const_reverse_iterator crbegin() const span_noexcept + { + return const_reverse_iterator ( cend() ); + } + + span_constexpr const_reverse_iterator crend() const span_noexcept + { + return const_reverse_iterator( cbegin() ); + } + +private: + + // Note: C++20 has std::pointer_traits::to_address( it ); + +#if span_HAVE( ITERATOR_CTOR ) + static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept + { + return nullptr; + } + + template< typename U > + static inline span_constexpr U * to_address( U * p ) span_noexcept + { + return p; + } + + template< typename Ptr + span_REQUIRES_T(( ! std::is_pointer::value )) + > + static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept + { + return to_address( it.operator->() ); + } +#endif // span_HAVE( ITERATOR_CTOR ) + +private: + pointer data_; + size_type size_; +}; + +// class template argument deduction guides: + +#if span_HAVE( DEDUCTION_GUIDES ) + +template< class T, size_t N > +span( T (&)[N] ) -> span(N)>; + +template< class T, size_t N > +span( std::array & ) -> span(N)>; + +template< class T, size_t N > +span( std::array const & ) -> span(N)>; + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< class Container > +span( Container& ) -> span; + +template< class Container > +span( Container const & ) -> span; + +#endif + +// iterator: constraints: It satisfies contiguous_­iterator. + +template< class It, class EndOrSize > +span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t >::type >; + +#endif // span_HAVE( DEDUCTION_GUIDES ) + +// 26.7.3.7 Comparison operators [span.comparison] + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool same( span const & l, span const & r ) span_noexcept +{ + return std11::is_same::value + && l.size() == r.size() + && static_cast( l.data() ) == r.data(); +} + +#endif + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator==( span const & l, span const & r ) +{ + return +#if span_FEATURE( SAME ) + same( l, r ) || +#endif + ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<( span const & l, span const & r ) +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator!=( span const & l, span const & r ) +{ + return !( l == r ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<=( span const & l, span const & r ) +{ + return !( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>( span const & l, span const & r ) +{ + return ( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>=( span const & l, span const & r ) +{ + return !( l < r ); +} + +#endif // span_FEATURE( COMPARISON ) + +// 26.7.2.6 views of object representation [span.objectrep] + +#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow: + +template< typename T, extent_t Extent > +struct BytesExtent +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = span_sizeof(T) * Extent }; +#else + enum ET { value = span_sizeof(T) * Extent }; +#endif +}; + +template< typename T > +struct BytesExtent< T, dynamic_extent > +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = dynamic_extent }; +#else + enum ET { value = dynamic_extent }; +#endif +}; + +template< class T, extent_t Extent > +inline span_constexpr span< const std17::byte, BytesExtent::value > +as_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() }; +#else + return span< const std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +template< class T, extent_t Extent > +inline span_constexpr span< std17::byte, BytesExtent::value > +as_writable_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() }; +#else + return span< std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// extensions: non-member views: +// this feature implies the presence of make_span() + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB ) && span_CPP11_120 + +template< extent_t Count, class T > +span_constexpr auto +first( T & t ) -> decltype( make_span(t).template first() ) +{ + return make_span( t ).template first(); +} + +template< class T > +span_constexpr auto +first( T & t, size_t count ) -> decltype( make_span(t).first(count) ) +{ + return make_span( t ).first( count ); +} + +template< extent_t Count, class T > +span_constexpr auto +last( T & t ) -> decltype( make_span(t).template last() ) +{ + return make_span(t).template last(); +} + +template< class T > +span_constexpr auto +last( T & t, extent_t count ) -> decltype( make_span(t).last(count) ) +{ + return make_span( t ).last( count ); +} + +template< size_t Offset, extent_t Count = dynamic_extent, class T > +span_constexpr auto +subspan( T & t ) -> decltype( make_span(t).template subspan() ) +{ + return make_span( t ).template subspan(); +} + +template< class T > +span_constexpr auto +subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) ) +{ + return make_span( t ).subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB ) + +// 27.8 Container and view access [iterator.container] + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::size_t size( span const & spn ) +{ + return static_cast( spn.size() ); +} + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::ptrdiff_t ssize( span const & spn ) +{ + return static_cast( spn.size() ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { + +using span_lite::dynamic_extent; + +using span_lite::span; + +using span_lite::with_container; + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) +using span_lite::same; +#endif + +using span_lite::operator==; +using span_lite::operator!=; +using span_lite::operator<; +using span_lite::operator<=; +using span_lite::operator>; +using span_lite::operator>=; +#endif + +#if span_HAVE( BYTE ) +using span_lite::as_bytes; +using span_lite::as_writable_bytes; +#endif + +using span_lite::size; +using span_lite::ssize; + +} // namespace nonstd + +#endif // span_USES_STD_SPAN + +// make_span() [span-lite extension]: + +#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB ) + +#if span_USES_STD_SPAN +# define span_constexpr constexpr +# define span_noexcept noexcept +# define span_nullptr nullptr +# ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +# endif +using extent_t = span_CONFIG_EXTENT_TYPE; +#endif // span_USES_STD_SPAN + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr span +make_span( T * ptr, size_t count ) span_noexcept +{ + return span( ptr, count ); +} + +template< class T > +inline span_constexpr span +make_span( T * first, T * last ) span_noexcept +{ + return span( first, last ); +} + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( T ( &arr )[ N ] ) span_noexcept +{ + return span(N)>( &arr[ 0 ], N ); +} + +#if span_USES_STD_SPAN || span_HAVE( ARRAY ) + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( std::array< T, N > & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +template< class T, std::size_t N > +inline span_constexpr span< const T, static_cast(N) > +make_span( std::array< T, N > const & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +#endif // span_HAVE( ARRAY ) + +#if span_USES_STD_SPAN + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO ) + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#else + +template< class T > +inline span_constexpr span +make_span( span spn ) span_noexcept +{ + return spn; +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector const & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +#endif // span_USES_STD_SPAN || ( ... ) + +#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container & cont ) span_noexcept +{ + return span< typename Container::value_type >( with_container, cont ); +} + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container const & cont ) span_noexcept +{ + return span< const typename Container::value_type >( with_container, cont ); +} + +#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::make_span; +} // namespace nonstd + +#endif // #if span_FEATURE_TO_STD( MAKE_SPAN ) + +#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) ) + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr auto +byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) > +{ + return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) ); +} + +template< class T > +inline span_constexpr auto +byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) > +{ + return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::byte_span; +} // namespace nonstd + +#endif // span_FEATURE( BYTE_SPAN ) + +#if span_HAVE( STRUCT_BINDING ) + +#if span_CPP14_OR_GREATER +# include +#elif span_CPP11_OR_GREATER +# include +namespace std { + template< std::size_t I, typename T > + using tuple_element_t = typename tuple_element::type; +} +#else +namespace std { + template< typename T > + class tuple_size; /*undefined*/ + + template< std::size_t I, typename T > + class tuple_element; /* undefined */ +} +#endif // span_CPP14_OR_GREATER + +namespace std { + +// 26.7.X Tuple interface + +// std::tuple_size<>: + +template< typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_size< nonstd::span > : public integral_constant(Extent)> {}; + +// std::tuple_size<>: Leave undefined for dynamic extent: + +template< typename ElementType > +class tuple_size< nonstd::span >; + +// std::tuple_element<>: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_element< I, nonstd::span > +{ +public: +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element: dynamic extent or index out of range" ); +#endif + using type = ElementType; +}; + +// std::get<>(), 2 variants: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType & get( nonstd::span & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType const & get( nonstd::span const & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +} // end namespace std + +#endif // span_HAVE( STRUCT_BINDING ) + +#if ! span_USES_STD_SPAN +span_RESTORE_WARNINGS() +#endif // span_USES_STD_SPAN + +#endif // NONSTD_SPAN_HPP_INCLUDED From 0c939f45ddac2a243cdde88e80d0f1103d1a38b1 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 12 Apr 2021 09:26:48 +0200 Subject: [PATCH 2/6] ZmFont: Rework/modernize API --- src/CMakeLists.txt | 1 + src/zm_font.cpp | 154 ++++++++++++++++++++++++++++----------------- src/zm_font.h | 93 ++++++++++++++++++++------- src/zm_image.cpp | 25 ++++---- 4 files changed, 182 insertions(+), 91 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d9c4d92fd..281c7ba34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,6 +82,7 @@ target_link_libraries(zm libbcrypt::bcrypt jwt-cpp::jwt-cpp RtspServer::RtspServer + martinmoene::span-lite PRIVATE zm-core-interface) diff --git a/src/zm_font.cpp b/src/zm_font.cpp index 92a36501d..11ba405c8 100644 --- a/src/zm_font.cpp +++ b/src/zm_font.cpp @@ -1,77 +1,119 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + #include "zm_font.h" #include -#include +#include -int ZmFont::ReadFontFile(const std::string &loc) { - FILE *f = fopen(loc.c_str(), "rb"); - if ( !f ) return -1; // FILE NOT FOUND +constexpr uint8 FontVariant::kMaxNumCodePoints; +constexpr uint8 FontVariant::kMaxCharHeight; +constexpr uint8 FontVariant::kMaxCharWidth; - font = new ZMFONT; +FontVariant::FontVariant() : char_height_(0), char_width_(0), codepoint_count_(0) {} - size_t header_size = 8 + (sizeof(ZMFONT_BH) * NUM_FONT_SIZES); - - // MAGIC + pad + BitmapHeaders - size_t readsize = fread(&font[0], 1, header_size, f); - if ( readsize < header_size ) { - delete font; - font = nullptr; - fclose(f); - return -2; // EOF reached, invalid file +FontVariant::FontVariant(uint16 char_height, uint16 char_width, std::vector bitmap) + : char_height_(char_height), char_width_(char_width), bitmap_(std::move(bitmap)) { + if (char_height_ > kMaxCharHeight) { + throw std::invalid_argument("char_height > kMaxCharHeight"); } - if ( memcmp(font->MAGIC, "ZMFNT", 5) != 0 ) { // Check whether magic is correct - delete font; - font = nullptr; - fclose(f); - return -3; + if (char_width_ > kMaxCharWidth) { + throw std::invalid_argument("char_width > kMaxCharWidth"); } - struct stat st; - stat(loc.c_str(), &st); + if (bitmap_.size() % char_height_ != 0) { + throw std::invalid_argument("bitmap has wrong length"); + } - for ( int i = 0; i < NUM_FONT_SIZES; i++ ) { - /* Character Width cannot be greater than 64 as a row is represented as a uint64_t, - height cannot be greater than 200(arbitary number which i have chosen, shouldn't need more than this) and - idx should not be more than filesize - */ - if ( font->header[i].charWidth > 64 || font->header[i].charWidth == 0 || - font->header[i].charHeight > 200 || font->header[i].charHeight == 0 || - (font->header[i].idx > st.st_size) ) { - delete font; - font = nullptr; - fclose(f); - return -4; + codepoint_count_ = bitmap_.size() / char_height; +} + +nonstd::span FontVariant::GetCodepoint(uint8 idx) const { + static constexpr std::array empty_bitmap = {}; + + if (idx >= GetCodepointsCount()) { + return {empty_bitmap.begin(), GetCharHeight()}; + } + + return {bitmap_.begin() + (idx * GetCharHeight()), GetCharHeight()}; +} + +std::ifstream &operator>>(std::ifstream &stream, FontBitmapHeader &bm_header) { + stream.read(reinterpret_cast(&bm_header), sizeof(bm_header)); + + return stream; +} + +std::ifstream &operator>>(std::ifstream &stream, FontFileHeader &header) { + stream.read(header.magic, sizeof(header.magic)); + stream.seekg(sizeof(header.pad), std::ifstream::cur); + + for (FontBitmapHeader &bm_header : header.bitmap_header) + stream >> bm_header; + + return stream; +} + +FontLoadError ZmFont::LoadFontFile(const std::string &loc) { + std::ifstream font_file(loc, std::ifstream::binary); + font_file.exceptions(std::ifstream::badbit); + + if (!font_file.is_open()) { + return FontLoadError::kFileNotFound; + } + + FontFileHeader file_header = {}; + font_file >> file_header; + + if (font_file.fail()) { + return FontLoadError::kInvalidFile; + } + + if (memcmp(file_header.magic, "ZMFNT", 5) != 0) { + return FontLoadError::kInvalidFile; + } + + for (int i = 0; i < kNumFontSizes; i++) { + FontBitmapHeader bitmap_header = file_header.bitmap_header[i]; + + if (bitmap_header.char_width > FontVariant::kMaxCharWidth + || bitmap_header.char_height > FontVariant::kMaxCharHeight + || bitmap_header.number_of_code_points > FontVariant::kMaxNumCodePoints) { + return FontLoadError::kInvalidFile; } - } // end foreach font size - datasize = st.st_size - header_size; + std::vector bitmap; + bitmap.resize(bitmap_header.number_of_code_points * bitmap_header.char_height); - font->data = new uint64_t[datasize/sizeof(uint64_t)]; - readsize = fread(&font->data[0], 1, datasize, f); - if ( readsize < datasize ) { // Shouldn't happen - delete[] font->data; - font->data = nullptr; - delete font; - font = nullptr; - return -2; - } - fclose(f); - return 0; -} + std::size_t bitmap_bytes = bitmap.size() * sizeof(uint64); + font_file.read(reinterpret_cast(bitmap.data()), static_cast(bitmap_bytes)); -ZmFont::~ZmFont() { - if ( font && font->data ) { - delete[] font->data; - font->data = nullptr; + variants_[i] = + {bitmap_header.char_height, bitmap_header.char_width, std::move(bitmap)}; } - if ( font ) { - delete font; - font = nullptr; + if (font_file.fail()) { + return FontLoadError::kInvalidFile; } + + return FontLoadError::kOk; } -uint64_t *ZmFont::GetBitmapData() { - return &font->data[font->header[size].idx]; +const FontVariant &ZmFont::GetFontVariant(uint8 idx) const { + return variants_.at(idx); } diff --git a/src/zm_font.h b/src/zm_font.h index 22ecb4612..8ba9f7642 100644 --- a/src/zm_font.h +++ b/src/zm_font.h @@ -1,40 +1,89 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + #ifndef ZM_FONT_H #define ZM_FONT_H #include "zm_define.h" +#include +#include +#include "span.hpp" #include +#include -#define NUM_FONT_SIZES 4 +constexpr uint8 kNumFontSizes = 4; -struct ZMFONT_BH{ - uint16_t charHeight; // Height of every character - uint16_t charWidth; // Width of every character - uint32_t numberofCodePoints; // number of codepoints max 255 for now - uint32_t idx; // idx in data where data for the bitmap starts - uint32_t pad; // padding to round of the size +enum class FontLoadError { + kOk, + kFileNotFound, + kInvalidFile }; -struct ZMFONT { - char MAGIC[6]; // ZMFNT\0 - char pad[2]; - ZMFONT_BH header[NUM_FONT_SIZES]; - uint64_t *data; +#pragma pack(push, 1) +struct FontBitmapHeader { + uint16 char_height; // height of every character + uint16 char_width; // width of every character + uint32 number_of_code_points; // number of codepoints; max. 255 for now + uint32 idx; // offset in data where data for the bitmap starts; not used + uint32 pad; // padding +}; +#pragma pack(pop) + +#pragma pack(push, 1) +struct FontFileHeader { + char magic[6]; // "ZMFNT\0" + uint8 pad[2]; + std::array bitmap_header; +}; +#pragma pack(pop) + +class FontVariant { + public: + static constexpr uint8 kMaxNumCodePoints = 255; + // height cannot be greater than 200 (arbitrary number; shouldn't need more than this) + static constexpr uint8 kMaxCharHeight = 200; + // character width can't be greater than 64 as a row is represented as an uint64 + static constexpr uint8 kMaxCharWidth = 64; + + FontVariant(); + FontVariant(uint16 char_height, uint16 char_width, std::vector bitmap); + + uint16 GetCharHeight() const { return char_height_; } + uint16 GetCharWidth() const { return char_width_; } + uint8 GetCodepointsCount() const { return codepoint_count_; } + + // Returns the bitmap of the codepoint `idx`. If `idx` is greater than `GetCodepointsCount` + // a all-zero bitmap with `GetCharHeight` elements is returned. + nonstd::span GetCodepoint(uint8 idx) const; + + private: + uint16 char_height_; + uint16 char_width_; + uint8 codepoint_count_; + std::vector bitmap_; }; class ZmFont { public: - ~ZmFont(); - int ReadFontFile(const std::string &loc); - ZMFONT *GetFont() { return font; } - void SetFontSize(int _size) { size = _size; } - uint64_t *GetBitmapData(); - uint16_t GetCharWidth() { return font->header[size].charWidth; } - uint16_t GetCharHeight() { return font->header[size].charHeight; } + FontLoadError LoadFontFile(const std::string &loc); + const FontVariant &GetFontVariant(uint8 idx) const; private: - int size = 0; - size_t datasize = 0; - ZMFONT *font = nullptr; + std::array variants_; }; #endif diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 0ce62eb13..de5492fd4 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -564,10 +564,10 @@ void Image::Initialise() { g_u_table = g_u_table_global; b_u_table = b_u_table_global; - int res = font.ReadFontFile(config.font_file_location); - if ( res == -1 ) { + FontLoadError res = font.LoadFontFile(config.font_file_location); + if ( res == FontLoadError::kFileNotFound ) { Panic("Invalid font location: %s", config.font_file_location); - } else if ( res == -2 || res == -3 || res == -4 ) { + } else if ( res == FontLoadError::kInvalidFile ) { Panic("Invalid font file."); } initialised = true; @@ -1943,9 +1943,9 @@ const Coord Image::centreCoord( const char *text, int size=1 ) const { line_no++; } - font.SetFontSize(size-1); - uint16_t char_width = font.GetCharWidth(); - uint16_t char_height = font.GetCharHeight(); + FontVariant const &font_variant = font.GetFontVariant(size - 1); + uint16_t char_width = font_variant.GetCharWidth(); + uint16_t char_height = font_variant.GetCharHeight(); int x = (width - (max_line_len * char_width )) / 2; int y = (height - (line_no * char_height) ) / 2; return Coord(x, y); @@ -2023,10 +2023,9 @@ void Image::Annotate( const Rgb bg_rgb_col = rgb_convert(bg_colour, subpixelorder); const bool bg_trans = (bg_colour == kRGBTransparent); - font.SetFontSize(size-1); - const uint16_t char_width = font.GetCharWidth(); - const uint16_t char_height = font.GetCharHeight(); - const uint64_t *font_bitmap = font.GetBitmapData(); + FontVariant const &font_variant = font.GetFontVariant(size - 1); + const uint16_t char_width = font_variant.GetCharWidth(); + const uint16_t char_height = font_variant.GetCharHeight(); Debug(4, "Font size %d, char_width %d char_height %d", size, char_width, char_height); while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) { @@ -2064,7 +2063,7 @@ void Image::Annotate( for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += width ) { unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { - uint64_t f = font_bitmap[(line[c] * char_height) + r]; + uint64_t f = font_variant.GetCodepoint(line[c])[r]; if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width); while ( f != 0 ) { uint64_t t = f & -f; @@ -2081,7 +2080,7 @@ void Image::Annotate( for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) { unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { - uint64_t f = font_bitmap[(line[c] * char_height) + r]; + uint64_t f = font_variant.GetCodepoint(line[c])[r]; if ( !bg_trans ) { for ( int i = 0; i < char_width; i++ ) { // We need to set individual r,g,b components unsigned char *colour_ptr = temp_ptr + (i*3); @@ -2109,7 +2108,7 @@ void Image::Annotate( for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) { Rgb* temp_ptr = (Rgb*)ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { - uint64_t f = font_bitmap[(line[c] * char_height) + r]; + uint64_t f = font_variant.GetCodepoint(line[c])[r]; if ( !bg_trans ) { for ( int i = 0; i < char_width; i++ ) *(temp_ptr + i) = bg_rgb_col; From 4d14347c42af5578edd4e334a80a9d48c43bec79 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 12 Apr 2021 09:07:35 +0200 Subject: [PATCH 3/6] tests: Add tests for ZmFont --- tests/CMakeLists.txt | 7 + tests/data/fonts/01_bad_magic.zmfnt | Bin 0 -> 8 bytes tests/data/fonts/02_variant_invalid.zmfnt | Bin 0 -> 24 bytes tests/data/fonts/03_missing_cps.zmfnt | Bin 0 -> 2952 bytes tests/data/fonts/04_valid.zmfnt | Bin 0 -> 3752 bytes tests/data/fonts/generate_fonts.py | 54 ++++++++ tests/zm_font.cpp | 156 ++++++++++++++++++++++ 7 files changed, 217 insertions(+) create mode 100644 tests/data/fonts/01_bad_magic.zmfnt create mode 100644 tests/data/fonts/02_variant_invalid.zmfnt create mode 100644 tests/data/fonts/03_missing_cps.zmfnt create mode 100644 tests/data/fonts/04_valid.zmfnt create mode 100644 tests/data/fonts/generate_fonts.py create mode 100644 tests/zm_font.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c863c7cbb..e0c68aeff 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,7 @@ include(Catch) set(TEST_SOURCES zm_comms.cpp zm_crypt.cpp + zm_font.cpp zm_utils.cpp) add_executable(tests main.cpp ${TEST_SOURCES}) @@ -31,3 +32,9 @@ target_include_directories(tests ${CMAKE_CURRENT_BINARY_DIR}) catch_discover_tests(tests) + +add_custom_command(TARGET tests + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/data/ ${CMAKE_CURRENT_BINARY_DIR}/data/ + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/data/) diff --git a/tests/data/fonts/01_bad_magic.zmfnt b/tests/data/fonts/01_bad_magic.zmfnt new file mode 100644 index 0000000000000000000000000000000000000000..a73d2b51f77b75a39290ad9df7cab0fcf8a3412f GIT binary patch literal 8 PcmZ>Ca&~cLU|;|M2iO5n literal 0 HcmV?d00001 diff --git a/tests/data/fonts/02_variant_invalid.zmfnt b/tests/data/fonts/02_variant_invalid.zmfnt new file mode 100644 index 0000000000000000000000000000000000000000..9cbd0b0243ac06f63fc9ec2b40cec80721e1fe76 GIT binary patch literal 24 Ycma#@b@K~hU|=}O;K;zh2xLG305X;Wn*aa+ literal 0 HcmV?d00001 diff --git a/tests/data/fonts/03_missing_cps.zmfnt b/tests/data/fonts/03_missing_cps.zmfnt new file mode 100644 index 0000000000000000000000000000000000000000..b66e696ed1229e76f925d096b81f03bc740d5be3 GIT binary patch literal 2952 zcma#@b@K~hU|`?^Lm&eRVEhy`{s}a`4kJVWN->Pmqu~Jxfzfm@nhr+O!Dv1hEeA%+ Qfzfhcv>X^M2k@5z0GE~o$p8QV literal 0 HcmV?d00001 diff --git a/tests/data/fonts/04_valid.zmfnt b/tests/data/fonts/04_valid.zmfnt new file mode 100644 index 0000000000000000000000000000000000000000..3cb839931a69e8ef9b82db2b9865357aaae1d0d2 GIT binary patch literal 3752 zcmeIxAr^oj5QO1To4~aUzNg7#_Qak@mR+OK;PZzt4Dn;xmucN3jcwOMtfW+Lq$7G( zZvBj2R9c_AxF8Tf;8EcICZrSLfP)FVH3z-(igXAZa4>. + */ + +#include "catch2/catch.hpp" + +#include "zm_font.h" + +CATCH_REGISTER_ENUM(FontLoadError, + FontLoadError::kOk, + FontLoadError::kFileNotFound, + FontLoadError::kInvalidFile) + +TEST_CASE("FontVariant: construction") { + FontVariant variant; + + SECTION("default construction") { + REQUIRE(variant.GetCharHeight() == 0); + REQUIRE(variant.GetCharWidth() == 0); + } + + SECTION("values in range") { + constexpr uint8 height = 10; + constexpr uint8 width = 10; + std::vector bitmap(FontVariant::kMaxNumCodePoints * height); + + REQUIRE_NOTHROW(variant = FontVariant(height, width, bitmap)); + + REQUIRE(variant.GetCharHeight() == height); + REQUIRE(variant.GetCharWidth() == width); + REQUIRE(variant.GetCodepointsCount() == FontVariant::kMaxNumCodePoints); + } + + SECTION("height out of range") { + constexpr uint8 height = FontVariant::kMaxCharHeight + 1; + constexpr uint8 width = 10; + std::vector bitmap(FontVariant::kMaxNumCodePoints * height); + + REQUIRE_THROWS(variant = FontVariant(height, width, bitmap)); + } + + SECTION("width out of range") { + constexpr uint8 height = 10; + constexpr uint8 width = FontVariant::kMaxCharWidth + 1; + std::vector bitmap(FontVariant::kMaxNumCodePoints * height); + + REQUIRE_THROWS(variant = FontVariant(height, width, bitmap)); + } + + SECTION("bitmap of wrong size") { + constexpr uint8 height = 10; + constexpr uint8 width = 10; + std::vector bitmap(FontVariant::kMaxNumCodePoints * height + 1); + + REQUIRE_THROWS(variant = FontVariant(height, width, bitmap)); + } +} + +TEST_CASE("FontVariant: GetCodepoint") { + constexpr uint8 height = 10; + constexpr uint8 width = 10; + std::vector bitmap(FontVariant::kMaxNumCodePoints * height); + + // fill bitmap for each codepoint alternating with 1 and std::numeric_limits::max() + std::generate(bitmap.begin(), bitmap.end(), + [n = 0, zero = true]() mutable { + if (n == height) { + zero = !zero; + n = 0; + } + n++; + if (zero) { + return static_cast(1); + } else { + return std::numeric_limits::max(); + } + }); + + FontVariant variant(height, width, bitmap); + nonstd::span cp; + + SECTION("in bounds") { + cp = variant.GetCodepoint(0); + REQUIRE(std::all_of(cp.begin(), cp.end(), + [](uint64 l) { return l == 1; }) == true); + + cp = variant.GetCodepoint(1); + REQUIRE(std::all_of(cp.begin(), cp.end(), + [](uint64 l) { return l == std::numeric_limits::max(); }) == true); + } + + SECTION("out-of-bounds: all-zero bitmap") { + cp = variant.GetCodepoint(FontVariant::kMaxNumCodePoints); + REQUIRE(std::all_of(cp.begin(), cp.end(), + [](uint64 l) { return l == 0; }) == true); + } +} + +TEST_CASE("ZmFont: variants not loaded") { + ZmFont font; + + SECTION("returns empty variant") { + FontVariant variant; + REQUIRE_NOTHROW(variant = font.GetFontVariant(0)); + + REQUIRE(variant.GetCharHeight() == 0); + REQUIRE(variant.GetCharWidth() == 0); + REQUIRE(variant.GetCodepoint(0).empty() == true); + } + + SECTION("variant idx out-of-bounds") { + REQUIRE_THROWS(font.GetFontVariant(kNumFontSizes)); + } +} + +TEST_CASE("ZmFont: load font file") { + ZmFont font; + + SECTION("file not found") { + REQUIRE(font.LoadFontFile("does_not_exist.zmfnt") == FontLoadError::kFileNotFound); + } + + SECTION("invalid files") { + REQUIRE(font.LoadFontFile("data/fonts/01_bad_magic.zmfnt") == FontLoadError::kInvalidFile); + REQUIRE(font.LoadFontFile("data/fonts/02_variant_invalid.zmfnt") == FontLoadError::kInvalidFile); + REQUIRE(font.LoadFontFile("data/fonts/03_missing_cps.zmfnt") == FontLoadError::kInvalidFile); + } + + SECTION("valid file") { + REQUIRE(font.LoadFontFile("data/fonts/04_valid.zmfnt") == FontLoadError::kOk); + + uint8 var_idx = GENERATE(range(static_cast(0), kNumFontSizes)); + FontVariant variant = font.GetFontVariant(var_idx); + REQUIRE(variant.GetCharHeight() == 10 + var_idx); + REQUIRE(variant.GetCharWidth() == 10 + var_idx); + + uint8 cp_idx = + GENERATE_COPY(range(static_cast(0), variant.GetCodepointsCount())); + nonstd::span cp = variant.GetCodepoint(cp_idx); + REQUIRE(std::all_of(cp.begin(), cp.end(), + [=](uint64 l) { return l == var_idx; }) == true); + } +} From 07e49e47aff35e4b27c7179eb7124fa0f70f5876 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 19 Apr 2021 22:33:51 +0200 Subject: [PATCH 4/6] utils: Add a clamping function mimicking std::clamp This can be replaced with std::clamp in C++17. --- src/zm_utils.h | 11 +++++++++++ tests/zm_utils.cpp | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/src/zm_utils.h b/src/zm_utils.h index 4bfeedbf1..88e00a785 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,16 @@ typename std::enable_if::value && std::extent::value == 0, s template inline auto make_unique(Args &&...) -> typename std::enable_if::value != 0, void>::type = delete; + +template +constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp) { + return comp(v, lo) ? lo : comp(hi, v) ? hi : v; +} + +template +constexpr const T &clamp(const T &v, const T &lo, const T &hi) { + return clamp(v, lo, hi, std::less{}); +} } typedef std::chrono::microseconds Microseconds; diff --git a/tests/zm_utils.cpp b/tests/zm_utils.cpp index ffe7ce36f..408b9f43e 100644 --- a/tests/zm_utils.cpp +++ b/tests/zm_utils.cpp @@ -150,6 +150,12 @@ TEST_CASE("Base64Encode") { REQUIRE(Base64Encode("foobar") == "Zm9vYmFy"); } +TEST_CASE("ZM::clamp") { + REQUIRE(ZM::clamp(1, 0, 2) == 1); + REQUIRE(ZM::clamp(3, 0, 2) == 2); + REQUIRE(ZM::clamp(-1, 0, 2) == 0); +} + TEST_CASE("UriDecode") { REQUIRE(UriDecode("abcABC123-_.~%21%28%29%26%3d%20") == "abcABC123-_.~!()&= "); REQUIRE(UriDecode("abcABC123-_.~%21%28%29%26%3d+") == "abcABC123-_.~!()&= "); From a918e8aeba89d398ec66eb5122ec11c94efcfb43 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 19 Apr 2021 01:55:11 +0200 Subject: [PATCH 5/6] Image: Modernize Annotate method --- src/zm_image.cpp | 202 +++++++++++++++++++++-------------------------- src/zm_image.h | 12 ++- 2 files changed, 96 insertions(+), 118 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index de5492fd4..412d2d389 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -130,7 +130,6 @@ Image::Image() : buffer = 0; buffertype = ZM_BUFTYPE_DONTFREE; holdbuffer = 0; - text[0] = '\0'; blend = fptr_blend; } @@ -150,7 +149,6 @@ Image::Image(const char *filename) { buffertype = ZM_BUFTYPE_DONTFREE; holdbuffer = 0; ReadJpeg(filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); - text[0] = '\0'; update_function_pointers(); } @@ -176,7 +174,6 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint } else { AllocImgBuffer(size); } - text[0] = '\0'; imagePixFormat = AVPixFormat(); update_function_pointers(); @@ -204,14 +201,12 @@ Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_sub } else { AllocImgBuffer(size); } - text[0] = '\0'; imagePixFormat = AVPixFormat(); update_function_pointers(); } Image::Image(const AVFrame *frame) { - text[0] = '\0'; width = frame->width; height = frame->height; pixels = width*height; @@ -336,7 +331,7 @@ Image::Image(const Image &p_image) { holdbuffer = 0; AllocImgBuffer(size); (*fptr_imgbufcpy)(buffer, p_image.buffer, size); - strncpy(text, p_image.text, sizeof(text)); + annotation_ = p_image.annotation_; imagePixFormat = p_image.imagePixFormat; update_function_pointers(); } @@ -1191,8 +1186,8 @@ cinfo->out_color_space = JCS_RGB; cinfo->dct_method = JDCT_FASTEST; jpeg_start_compress(cinfo, TRUE); - if ( config.add_jpeg_comments && text[0] ) { - jpeg_write_marker(cinfo, JPEG_COM, (const JOCTET *)text, strlen(text)); + if ( config.add_jpeg_comments && !annotation_.empty() ) { + jpeg_write_marker(cinfo, JPEG_COM, reinterpret_cast(annotation_.c_str()), annotation_.size()); } // If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal // No timestamp just leave off the exif section. @@ -1997,143 +1992,122 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/ */ void Image::Annotate( - const char *p_text, + const std::string &text, const Coord &coord, - const unsigned int size, + const uint8 size, const Rgb fg_colour, const Rgb bg_colour) { - strncpy(text, p_text, sizeof(text)-1); + annotation_ = text; - unsigned int index = 0; - unsigned int line_no = 0; - unsigned int text_len = strlen(text); - unsigned int line_len = 0; - const char *line = text; - - const uint8_t fg_r_col = RED_VAL_RGBA(fg_colour); - const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour); - const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour); - const uint8_t fg_bw_col = fg_colour & 0xff; const Rgb fg_rgb_col = rgb_convert(fg_colour, subpixelorder); - - const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour); - const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour); - const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour); - const uint8_t bg_bw_col = bg_colour & 0xff; const Rgb bg_rgb_col = rgb_convert(bg_colour, subpixelorder); - const bool bg_trans = (bg_colour == kRGBTransparent); FontVariant const &font_variant = font.GetFontVariant(size - 1); - const uint16_t char_width = font_variant.GetCharWidth(); - const uint16_t char_height = font_variant.GetCharHeight(); - Debug(4, "Font size %d, char_width %d char_height %d", size, char_width, char_height); + const uint16 char_width = font_variant.GetCharWidth(); + const uint16 char_height = font_variant.GetCharHeight(); - while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) { - unsigned int line_width = line_len * char_width; + std::vector lines = Split(annotation_, '\n'); + std::size_t max_line_length = 0; + for (const std::string &s : lines) { + max_line_length = std::max(max_line_length, s.size()); + } - unsigned int lo_line_x = coord.X(); - unsigned int lo_line_y = coord.Y() + (line_no * char_height); + uint32 x0_max = width - (max_line_length * char_width); + uint32 y0_max = height - (lines.size() * char_height); - unsigned int min_line_x = 0; - // FIXME What if line_width > width? - unsigned int max_line_x = width - line_width; - unsigned int min_line_y = 0; - unsigned int max_line_y = height - char_height; + // Calculate initial coordinates of annotation so that everything is displayed even if the + // user set coordinates would prevent that. + uint32 x0 = ZM::clamp(static_cast(coord.X()), 0u, x0_max); + uint32 y0 = ZM::clamp(static_cast(coord.Y()), 0u, y0_max); - if ( lo_line_x > max_line_x ) - lo_line_x = max_line_x; - if ( lo_line_x < min_line_x ) - lo_line_x = min_line_x; - if ( lo_line_y > max_line_y ) - lo_line_y = max_line_y; - if ( lo_line_y < min_line_y ) - lo_line_y = min_line_y; + uint32 y = y0; + for (const std::string &line : lines) { + uint32 x = x0; - unsigned int hi_line_x = lo_line_x + line_width; - unsigned int hi_line_y = lo_line_y + char_height; - - // Clip anything that runs off the right of the screen - if ( hi_line_x > width ) - hi_line_x = width; - if ( hi_line_y > height ) - hi_line_y = height; - - if ( colours == ZM_COLOUR_GRAY8 ) { - unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x]; - for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += width ) { - unsigned char *temp_ptr = ptr; - for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { - uint64_t f = font_variant.GetCodepoint(line[c])[r]; - if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width); - while ( f != 0 ) { - uint64_t t = f & -f; - int idx = char_width - __builtin_ctzll(f) + size; - *(temp_ptr + idx) = fg_bw_col; - f ^= t; + if (colours == ZM_COLOUR_GRAY8) { + uint8 *ptr = &buffer[(y * width) + x0]; + for (char c : line) { + for (uint64 cp_row : font_variant.GetCodepoint(c)) { + if (bg_colour != kRGBTransparent) { + std::fill(ptr, ptr + char_width, static_cast(bg_colour & 0xff)); } - temp_ptr += char_width; + + while (cp_row != 0) { + int column_idx = char_width - __builtin_ctzll(cp_row) + size; + *(ptr + column_idx) = fg_colour & 0xff; + cp_row = cp_row & (cp_row - 1); + } + } + ptr -= (width * char_height); + ptr += char_width; + x += char_width; + if (x >= width) { + break; } } - } else if ( colours == ZM_COLOUR_RGB24 ) { - unsigned int wc = width * colours; - unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours]; - for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) { - unsigned char *temp_ptr = ptr; - for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { - uint64_t f = font_variant.GetCodepoint(line[c])[r]; - if ( !bg_trans ) { - for ( int i = 0; i < char_width; i++ ) { // We need to set individual r,g,b components - unsigned char *colour_ptr = temp_ptr + (i*3); - RED_PTR_RGBA(colour_ptr) = bg_r_col; - GREEN_PTR_RGBA(colour_ptr) = bg_g_col; - BLUE_PTR_RGBA(colour_ptr) = bg_b_col; + } else if (colours == ZM_COLOUR_RGB24) { + constexpr uint8 bytesPerPixel = 3; + uint8 *ptr = &buffer[((y * width) + x0) * bytesPerPixel]; + + for (char c : line) { + for (uint64 cp_row : font_variant.GetCodepoint(c)) { + if (bg_colour != kRGBTransparent) { + for (int i = 0; i < char_width; i++) { // We need to set individual r,g,b components + uint8 *colour_ptr = ptr + (i * bytesPerPixel); + RED_PTR_RGBA(colour_ptr) = RED_VAL_RGBA(bg_colour); + GREEN_PTR_RGBA(colour_ptr) = GREEN_VAL_RGBA(bg_colour); + BLUE_PTR_RGBA(colour_ptr) = BLUE_VAL_RGBA(bg_colour); } } - while ( f != 0 ) { - uint64_t t = f & -f; - int idx = char_width - __builtin_ctzll(f) + size; - unsigned char *colour_ptr = temp_ptr + (idx*3); - RED_PTR_RGBA(colour_ptr) = fg_r_col; - GREEN_PTR_RGBA(colour_ptr) = fg_g_col; - BLUE_PTR_RGBA(colour_ptr) = fg_b_col; - f ^= t; + + while (cp_row != 0) { + int column_idx = char_width - __builtin_ctzll(cp_row) + size; + uint8 *colour_ptr = ptr + (column_idx * bytesPerPixel); + RED_PTR_RGBA(colour_ptr) = RED_VAL_RGBA(fg_colour); + GREEN_PTR_RGBA(colour_ptr) = GREEN_VAL_RGBA(fg_colour); + BLUE_PTR_RGBA(colour_ptr) = BLUE_VAL_RGBA(fg_colour); + cp_row = cp_row & (cp_row - 1); } - temp_ptr += char_width * colours; + } + ptr -= (width * char_height * bytesPerPixel); + ptr += char_width * bytesPerPixel; + x += char_width; + if (x >= width) { + break; } } - } else if ( colours == ZM_COLOUR_RGB32 ) { - unsigned int wc = width * colours; + } else if (colours == ZM_COLOUR_RGB32) { + constexpr uint8 bytesPerPixel = 4; + Rgb *ptr = reinterpret_cast(&buffer[((y * width) + x0) * bytesPerPixel]); - uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x) << 2]; - for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) { - Rgb* temp_ptr = (Rgb*)ptr; - for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { - uint64_t f = font_variant.GetCodepoint(line[c])[r]; - if ( !bg_trans ) { - for ( int i = 0; i < char_width; i++ ) - *(temp_ptr + i) = bg_rgb_col; + for (char c : line) { + for (uint64 cp_row : font_variant.GetCodepoint(c)) { + if (bg_colour != kRGBTransparent) { + std::fill(ptr, ptr + char_width, bg_rgb_col); } - while ( f != 0 ) { - uint64_t t = f & -f; - int idx = char_width - __builtin_ctzll(f) + size; - *(temp_ptr + idx) = fg_rgb_col; - f ^= t; + + while (cp_row != 0) { + uint32 column_idx = char_width - __builtin_ctzll(cp_row) + size; + *(ptr + column_idx) = fg_rgb_col; + cp_row = cp_row & (cp_row - 1); } - temp_ptr += char_width; + ptr += width; + } + ptr -= (width * char_height); + ptr += char_width; + x += char_width; + if (x >= width) { + break; } } - } else { Error("Annotate called with unexpected colours: %d", colours); return; } - - index += line_len; - while ( text[index] == '\n' ) { - index++; + y += char_height; + if (y >= height) { + break; } - line = text+index; - line_no++; } } diff --git a/src/zm_image.h b/src/zm_image.h index 848d0b6b7..671cc0487 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -115,7 +115,7 @@ class Image { inline void AllocImgBuffer(size_t p_bufsize) { - if ( buffer ) + if ( buffer ) DumpImgBuffer(); buffer = AllocBuffer(p_bufsize); @@ -152,7 +152,7 @@ class Image { uint8_t *buffer; int buffertype; /* 0=not ours, no need to call free(), 1=malloc() buffer, 2=new buffer */ int holdbuffer; /* Hold the buffer instead of replacing it with new one */ - char text[1024]; + std::string annotation_; public: Image(); @@ -254,7 +254,7 @@ class Image { bool ReadJpeg(const char *filename, unsigned int p_colours, unsigned int p_subpixelorder); bool WriteJpeg(const char *filename) const; - bool WriteJpeg(const char *filename, bool on_blocking_abort) const; + bool WriteJpeg(const char *filename, bool on_blocking_abort) const; bool WriteJpeg(const char *filename, int quality_override) const; bool WriteJpeg(const char *filename, struct timeval timestamp) const; bool WriteJpeg(const char *filename, int quality_override, struct timeval timestamp) const; @@ -282,7 +282,11 @@ class Image { const Coord centreCoord(const char *text, const int size) const; void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 ); - void Annotate(const char *p_text, const Coord &coord, unsigned int size = 1, Rgb fg_colour = kRGBWhite, Rgb bg_colour = kRGBBlack); + void Annotate(const std::string &text, + const Coord &coord, + uint8 size = 1, + Rgb fg_colour = kRGBWhite, + Rgb bg_colour = kRGBBlack); Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 ); //Image *HighlightEdges( Rgb colour, const Polygon &polygon ); void Timestamp( const char *label, const time_t when, const Coord &coord, const int size ); From 3020acf994f792ef0ca1ba2e7fedbb66bf362d0b Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 24 Apr 2021 00:54:41 +0200 Subject: [PATCH 6/6] ZmFont: Store character padding in font file The size/variant specific character padding should be stored with the font data. Modify the FontBitmapHeader accordingly and introduce a version field in the FontFileHeader so we are able to check we have a font file with the right structure associated with that version. The version field is set to 1 in this changeset. --- fonts/default.zmfnt | Bin 224472 -> 224472 bytes src/zm_font.cpp | 13 ++++++++----- src/zm_font.h | 10 +++++++--- src/zm_image.cpp | 6 +++--- tests/data/fonts/01_bad_magic.zmfnt | Bin 8 -> 8 bytes tests/data/fonts/02_variant_invalid.zmfnt | Bin 24 -> 24 bytes tests/data/fonts/03_missing_cps.zmfnt | Bin 2952 -> 2952 bytes tests/data/fonts/04_valid.zmfnt | Bin 3752 -> 3752 bytes tests/data/fonts/generate_fonts.py | 15 +++++++++------ tests/zm_font.cpp | 15 ++++++++++----- 10 files changed, 37 insertions(+), 22 deletions(-) diff --git a/fonts/default.zmfnt b/fonts/default.zmfnt index e74a798a007b80ef897344b8536712cd1153ecc9..e13998a393b39fdda5dca5c855378df857a7ef9b 100644 GIT binary patch delta 100 zcmca{llR6=Ue_pJH@^@DMh0#MHirKU5Wone#29$M{I6UL3`{`2B7+c^e_w%tff>lx kVUPgx_c<~!uxt$EXPTJ6)11K6p1{Ni#7x^0n3yZF0OJ}H#{d8T delta 97 zcmca{llR6=UZ*HuH@^@D1_o{hHirK|8V1A|c)w}fxkI{sXc*-5r~<#ConNrWB~xVaud7& diff --git a/src/zm_font.cpp b/src/zm_font.cpp index 11ba405c8..7ecaaa6b1 100644 --- a/src/zm_font.cpp +++ b/src/zm_font.cpp @@ -20,14 +20,16 @@ #include #include +constexpr uint8 kRequiredZmFntVersion = 1; + constexpr uint8 FontVariant::kMaxNumCodePoints; constexpr uint8 FontVariant::kMaxCharHeight; constexpr uint8 FontVariant::kMaxCharWidth; -FontVariant::FontVariant() : char_height_(0), char_width_(0), codepoint_count_(0) {} +FontVariant::FontVariant() : char_height_(0), char_width_(0), char_padding_(0), codepoint_count_(0) {} -FontVariant::FontVariant(uint16 char_height, uint16 char_width, std::vector bitmap) - : char_height_(char_height), char_width_(char_width), bitmap_(std::move(bitmap)) { +FontVariant::FontVariant(uint16 char_height, uint16 char_width, uint8 char_padding, std::vector bitmap) + : char_height_(char_height), char_width_(char_width), char_padding_(char_padding), bitmap_(std::move(bitmap)) { if (char_height_ > kMaxCharHeight) { throw std::invalid_argument("char_height > kMaxCharHeight"); } @@ -61,6 +63,7 @@ std::ifstream &operator>>(std::ifstream &stream, FontBitmapHeader &bm_header) { std::ifstream &operator>>(std::ifstream &stream, FontFileHeader &header) { stream.read(header.magic, sizeof(header.magic)); + stream.read(reinterpret_cast(&header.version), sizeof(header.version)); stream.seekg(sizeof(header.pad), std::ifstream::cur); for (FontBitmapHeader &bm_header : header.bitmap_header) @@ -84,7 +87,7 @@ FontLoadError ZmFont::LoadFontFile(const std::string &loc) { return FontLoadError::kInvalidFile; } - if (memcmp(file_header.magic, "ZMFNT", 5) != 0) { + if (memcmp(file_header.magic, "ZMFNT", 5) != 0 || file_header.version != kRequiredZmFntVersion) { return FontLoadError::kInvalidFile; } @@ -104,7 +107,7 @@ FontLoadError ZmFont::LoadFontFile(const std::string &loc) { font_file.read(reinterpret_cast(bitmap.data()), static_cast(bitmap_bytes)); variants_[i] = - {bitmap_header.char_height, bitmap_header.char_width, std::move(bitmap)}; + {bitmap_header.char_height, bitmap_header.char_width, bitmap_header.char_padding, std::move(bitmap)}; } if (font_file.fail()) { diff --git a/src/zm_font.h b/src/zm_font.h index 8ba9f7642..b606108b2 100644 --- a/src/zm_font.h +++ b/src/zm_font.h @@ -39,14 +39,16 @@ struct FontBitmapHeader { uint16 char_width; // width of every character uint32 number_of_code_points; // number of codepoints; max. 255 for now uint32 idx; // offset in data where data for the bitmap starts; not used - uint32 pad; // padding + uint8 char_padding; // padding around characters + uint8 pad[3]; // struct padding }; #pragma pack(pop) #pragma pack(push, 1) struct FontFileHeader { char magic[6]; // "ZMFNT\0" - uint8 pad[2]; + uint8 version; + uint8 pad; std::array bitmap_header; }; #pragma pack(pop) @@ -60,10 +62,11 @@ class FontVariant { static constexpr uint8 kMaxCharWidth = 64; FontVariant(); - FontVariant(uint16 char_height, uint16 char_width, std::vector bitmap); + FontVariant(uint16 char_height, uint16 char_width, uint8 char_padding, std::vector bitmap); uint16 GetCharHeight() const { return char_height_; } uint16 GetCharWidth() const { return char_width_; } + uint8 GetCharPadding() const { return char_padding_; } uint8 GetCodepointsCount() const { return codepoint_count_; } // Returns the bitmap of the codepoint `idx`. If `idx` is greater than `GetCodepointsCount` @@ -73,6 +76,7 @@ class FontVariant { private: uint16 char_height_; uint16 char_width_; + uint8 char_padding_; uint8 codepoint_count_; std::vector bitmap_; }; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 412d2d389..08d803a4c 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2033,7 +2033,7 @@ void Image::Annotate( } while (cp_row != 0) { - int column_idx = char_width - __builtin_ctzll(cp_row) + size; + int column_idx = char_width - __builtin_ctzll(cp_row) + font_variant.GetCharPadding(); *(ptr + column_idx) = fg_colour & 0xff; cp_row = cp_row & (cp_row - 1); } @@ -2061,7 +2061,7 @@ void Image::Annotate( } while (cp_row != 0) { - int column_idx = char_width - __builtin_ctzll(cp_row) + size; + int column_idx = char_width - __builtin_ctzll(cp_row) + font_variant.GetCharPadding(); uint8 *colour_ptr = ptr + (column_idx * bytesPerPixel); RED_PTR_RGBA(colour_ptr) = RED_VAL_RGBA(fg_colour); GREEN_PTR_RGBA(colour_ptr) = GREEN_VAL_RGBA(fg_colour); @@ -2087,7 +2087,7 @@ void Image::Annotate( } while (cp_row != 0) { - uint32 column_idx = char_width - __builtin_ctzll(cp_row) + size; + uint32 column_idx = char_width - __builtin_ctzll(cp_row) + font_variant.GetCharPadding(); *(ptr + column_idx) = fg_rgb_col; cp_row = cp_row & (cp_row - 1); } diff --git a/tests/data/fonts/01_bad_magic.zmfnt b/tests/data/fonts/01_bad_magic.zmfnt index a73d2b51f77b75a39290ad9df7cab0fcf8a3412f..2cfaf0818fbbe7f189eb6894343d7ab1766b2f60 100644 GIT binary patch literal 8 PcmZ>Ca&~cLU}OLQ2igHq literal 8 PcmZ>Ca&~cLU|;|M2iO5n diff --git a/tests/data/fonts/02_variant_invalid.zmfnt b/tests/data/fonts/02_variant_invalid.zmfnt index 9cbd0b0243ac06f63fc9ec2b40cec80721e1fe76..9b53e85abdf4532fe3c1110100c52548a0cbdeca 100644 GIT binary patch literal 24 Zcma#@b@K~hU}QMS;K%@^z<>!z0su0w0-gW> literal 24 Ycma#@b@K~hU|=}O;K;zh2xLG305X;Wn*aa+ diff --git a/tests/data/fonts/03_missing_cps.zmfnt b/tests/data/fonts/03_missing_cps.zmfnt index b66e696ed1229e76f925d096b81f03bc740d5be3..71c58970d217b5812a875acc5f2b71c76516024e 100644 GIT binary patch literal 2952 zcma#@b@K~hU}WF|Lk2Kl0+KL(3XqM=KY_;AVT8y7DKHqNN5cUW0;B0*G#!klgVB62 US`Lhs1Eb}@XgM%i4&W~b0R8v`(f|Me literal 2952 zcma#@b@K~hU|`?^Lm&eRVEhy`{s}a`4kJVWN->Pmqu~Jxfzfm@nhr+O!Dv1hEeA%+ Qfzfhcv>X^M2k@5z0GE~o$p8QV diff --git a/tests/data/fonts/04_valid.zmfnt b/tests/data/fonts/04_valid.zmfnt index 3cb839931a69e8ef9b82db2b9865357aaae1d0d2..0a4ad66536a646e1d63c3f04ae57e42ca0953dcb 100644 GIT binary patch delta 86 zcmZ1>yF%7A%Gb>=gn^NP3k(^+fC)%)10ffXP61*Np9cs*{7XyF%6}%Gb>=gn@y93k-n_DBxz`28u8+q(J#R3_M`|B`BYlffvm8V%!)g&O9-J Ke{%-212+I?{|M#) diff --git a/tests/data/fonts/generate_fonts.py b/tests/data/fonts/generate_fonts.py index 8fe24be8c..275c2021a 100644 --- a/tests/data/fonts/generate_fonts.py +++ b/tests/data/fonts/generate_fonts.py @@ -6,6 +6,8 @@ GOOD_MAGIC = b"ZMFNT\0" BAD_MAGIC = b"ABCDE\0" NUM_FONT_SIZES = 4 +ZMFNT_VERSION = 1 + class FontFile: def __init__(self, path): @@ -14,11 +16,12 @@ class FontFile: def write_file_header(self, magic): with open(self.path, "wb") as f: f.write(magic) - f.write(struct.pack("BB", 0, 0)) # pad + f.write(struct.pack("B", ZMFNT_VERSION)) + f.write(struct.pack("B", 0)) # pad - def write_bm_header(self, height, width, cp_count, idx): + def write_bm_header(self, height, width, cp_count, idx, padding): with open(self.path, "ab") as f: - f.write(struct.pack("HHIII", height, width, cp_count, idx, 0)) + f.write(struct.pack("HHIIBBBB", height, width, cp_count, idx, padding, 0, 0, 0)) def write_codepoints(self, value, height, count): with open(self.path, "ab") as f: @@ -32,14 +35,14 @@ font.write_file_header(BAD_MAGIC) # height, width and number of codepoints out of bounds font = FontFile("02_variant_invalid.zmfnt") font.write_file_header(GOOD_MAGIC) -font.write_bm_header(201, 65, 256, 0) +font.write_bm_header(201, 65, 256, 0, 2) # mismatch between number of codepoints specified in header and actually stored ones font = FontFile("03_missing_cps.zmfnt") font.write_file_header(GOOD_MAGIC) offs = 0 for _ in range(NUM_FONT_SIZES): - font.write_bm_header(10, 10, 10, offs) + font.write_bm_header(10, 10, 10, offs, 2) offs += 10 * 10 for _ in range(NUM_FONT_SIZES): font.write_codepoints(1, 10, 9) @@ -48,7 +51,7 @@ font = FontFile("04_valid.zmfnt") font.write_file_header(GOOD_MAGIC) offs = 0 for i in range(NUM_FONT_SIZES): - font.write_bm_header(10 + i, 10 + i, 10, offs) + font.write_bm_header(10 + i, 10 + i, 10, offs, 2) offs += 10 * (10 + i) for i in range(NUM_FONT_SIZES): font.write_codepoints(i, 10 + i, 10) diff --git a/tests/zm_font.cpp b/tests/zm_font.cpp index 3f4d6010e..84300469a 100644 --- a/tests/zm_font.cpp +++ b/tests/zm_font.cpp @@ -35,9 +35,10 @@ TEST_CASE("FontVariant: construction") { SECTION("values in range") { constexpr uint8 height = 10; constexpr uint8 width = 10; + constexpr uint8 padding = 2; std::vector bitmap(FontVariant::kMaxNumCodePoints * height); - REQUIRE_NOTHROW(variant = FontVariant(height, width, bitmap)); + REQUIRE_NOTHROW(variant = FontVariant(height, width, padding, bitmap)); REQUIRE(variant.GetCharHeight() == height); REQUIRE(variant.GetCharWidth() == width); @@ -47,31 +48,35 @@ TEST_CASE("FontVariant: construction") { SECTION("height out of range") { constexpr uint8 height = FontVariant::kMaxCharHeight + 1; constexpr uint8 width = 10; + constexpr uint8 padding = 2; std::vector bitmap(FontVariant::kMaxNumCodePoints * height); - REQUIRE_THROWS(variant = FontVariant(height, width, bitmap)); + REQUIRE_THROWS(variant = FontVariant(height, width, padding, bitmap)); } SECTION("width out of range") { constexpr uint8 height = 10; constexpr uint8 width = FontVariant::kMaxCharWidth + 1; + constexpr uint8 padding = 2; std::vector bitmap(FontVariant::kMaxNumCodePoints * height); - REQUIRE_THROWS(variant = FontVariant(height, width, bitmap)); + REQUIRE_THROWS(variant = FontVariant(height, width, padding, bitmap)); } SECTION("bitmap of wrong size") { constexpr uint8 height = 10; constexpr uint8 width = 10; + constexpr uint8 padding = 2; std::vector bitmap(FontVariant::kMaxNumCodePoints * height + 1); - REQUIRE_THROWS(variant = FontVariant(height, width, bitmap)); + REQUIRE_THROWS(variant = FontVariant(height, width, padding, bitmap)); } } TEST_CASE("FontVariant: GetCodepoint") { constexpr uint8 height = 10; constexpr uint8 width = 10; + constexpr uint8 padding = 2; std::vector bitmap(FontVariant::kMaxNumCodePoints * height); // fill bitmap for each codepoint alternating with 1 and std::numeric_limits::max() @@ -89,7 +94,7 @@ TEST_CASE("FontVariant: GetCodepoint") { } }); - FontVariant variant(height, width, bitmap); + FontVariant variant(height, width, padding, bitmap); nonstd::span cp; SECTION("in bounds") {