diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7bb88560b..eeb2e83b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@
# Project setup
########################################################################
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.12)
project(Trunk-Recorder LANGUAGES CXX C VERSION "4.6.0")
configure_file(cmake.h.in "${PROJECT_BINARY_DIR}/cmake.h" @ONLY)
@@ -248,6 +248,7 @@ list(APPEND trunk_recorder_sources
trunk-recorder/recorders/p25_recorder_qpsk_demod.cc
trunk-recorder/recorders/p25_recorder_decode.cc
trunk-recorder/csv_helper.cc
+ trunk-recorder/config.cc
trunk-recorder/talkgroup.cc
trunk-recorder/talkgroups.cc
trunk-recorder/unit_tag.cc
@@ -280,6 +281,11 @@ list(APPEND trunk_recorder_sources
trunk-recorder/call.h
trunk-recorder/plugin_manager/plugin_api.h
)
+
+ list(APPEND 3p_headers
+ lib/nlohmann/json.hpp
+ lib/csv-parser/csv.hpp
+ )
set_source_files_properties(lib/lfsr/lfsr.cxx COMPILE_FLAGS "-w")
@@ -293,6 +299,15 @@ include(GNUInstallDirs)
add_subdirectory(lib/op25_repeater)
+#set(JSON_Install OFF CACHE INTERNAL "")
+
+#add_subdirectory(lib/nlohmann_json)
+
+#include(FetchContent)
+
+#FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz)
+#FetchContent_MakeAvailable(json)
+
#target_compile_options(
# imbe_vocoder PRIVATE
# "-w"
@@ -312,7 +327,7 @@ set_property(
set_target_properties(trunk_recorder_library PROPERTIES OUTPUT_NAME "trunk-recorder")
set_target_properties(trunk_recorder_library PROPERTIES PUBLIC_HEADER "${trunk_recorder_headers}")
-
+set_target_properties(trunk_recorder_library PROPERTIES PRIVATE_HEADER "${3p_headers}")
# Weird - when I add LIBRARY before the DESTINATION, it installs in the /usr/local/lib dir on a mac
install(TARGETS trunk_recorder_library
@@ -340,7 +355,9 @@ add_subdirectory(plugins/simplestream)
add_executable(trunk-recorder trunk-recorder/main.cc) # ${trunk_recorder_sources})
-target_link_libraries(trunk-recorder git trunk_recorder_library gnuradio-op25_repeater ${CMAKE_DL_LIBS} ssl crypto ${CURL_LIBRARIES} ${Boost_LIBRARIES} ${GNURADIO_PMT_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES} ${GNURADIO_FILTER_LIBRARIES} ${GNURADIO_DIGITAL_LIBRARIES} ${GNURADIO_ANALOG_LIBRARIES} ${GNURADIO_AUDIO_LIBRARIES} ${GNURADIO_UHD_LIBRARIES} ${UHD_LIBRARIES} ${GNURADIO_BLOCKS_LIBRARIES} ${GNURADIO_OSMOSDR_LIBRARIES} ) # gRPC::grpc++_reflection protobuf::libprotobuf)
+target_link_libraries(trunk-recorder git trunk_recorder_library gnuradio-op25_repeater ${CMAKE_DL_LIBS} ssl crypto ${CURL_LIBRARIES} ${Boost_LIBRARIES} ${GNURADIO_PMT_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES} ${GNURADIO_FILTER_LIBRARIES} ${GNURADIO_DIGITAL_LIBRARIES} ${GNURADIO_ANALOG_LIBRARIES} ${GNURADIO_AUDIO_LIBRARIES} ${GNURADIO_UHD_LIBRARIES} ${UHD_LIBRARIES} ${GNURADIO_BLOCKS_LIBRARIES} ${GNURADIO_OSMOSDR_LIBRARIES} ) # gRPC::grpc++_reflection protobuf::libprotobuf)
+
+#target_link_libraries(trunk-recorder PRIVATE nlohmann_json::nlohmann_json )
message(STATUS "All libraries:" ${GNURADIO_ALL_LIBRARIES})
if(NOT Gnuradio_VERSION VERSION_LESS "3.8")
diff --git a/docs/CONFIGURE.md b/docs/CONFIGURE.md
index 95bb6c9cd..6bfe00dc9 100644
--- a/docs/CONFIGURE.md
+++ b/docs/CONFIGURE.md
@@ -411,22 +411,34 @@ The matching simplestream config to send audio from talkgroup 58918 to TCP port
This file provides info on the different talkgroups in a trunking system. A lot of this info can be found on the [Radio Reference](http://www.radioreference.com/) website. You need to be a Radio Reference member to download the table for your system preformatted as a CSV file. You can also try clicking on the "List All in one table" link, selecting everything in the table and copying it into a spreadsheet program, and then exporting or saving as a CSV file.
-**Note:** You can use the direct CSV from Radio Reference for talk groups, but setting priority is not supported with this file format. If you need to use the Priority field, you'll need to reorder the CSV to match the format described below.
+**Note:** You can use the direct CSV from Radio Reference for talk groups. You will need to add the Priority column if you are going to be using that.
-You may add an additional column that adds a priority for each talkgroup. The priority field specifies the number of recorders the system must have available to record a new call for the talkgroup. For example, a priority of 1, the highest means as long as at least a single recorder is available, the system will record the new call. If the priority is 2, the system would at least 2 free recorders to record the new call, and so on. If there is no priority set for a talkgroup entry, a prioity of 1 is assumed.
+A Header row is required on the first line of the CSV file. The supported column names are: "Decimal", "Mode", "Description","Alpha Tag", "Hex", "Category", "Tag", "Priority", "Preferred NAC"
-Talkgroups assigned a priority of -1 will never be recorded, regardless of the number of available recorders.
+The first column must be the "Decimal" column.
-Trunk Recorder really only uses the priority information and the decimal talkgroup ID. The Website uses the same file though to help display information about each talkgroup.
+The columns are:
-Here are the column headers and some sample data: NOTE: If you are adding the Priority to a RR csv, as well as changing order you must use a heading for the first column other than "Decimal" eg DEC for TR to detect you are supplying this layout.
+| Column Name | Required | Value |
+|-------------|----------|-------|
+| Decimal | ✔️ | The Talkgroup Number formatted as a decimal number. |
+| Mode | ✔️ | Mode defines the type of transmission broadcast on this talkgroup. Analog transmissions are standard voice, Digital and TDMA transmissions are digitally modulated.
A = Analog Talkgroup
D = Digital Talkgroup
M = Mixed Analog/Digital
T = TDMA Capable Talkgroup
--
A trailing lowercase e represents partial encryption. A trailing uppercase E represents full encryption. |
+| Description | ✔️ | The description of the talkgroup |
+| Alpha Tag | | A 16 character description that is intended as a shortened display on radio displays |
+| Hex | | The Talkgroup Number formatted as a hex number. This value is currently not used. |
+| Category | | The category for the Talkgroup |
+| Tag | | The Service Tag for the Talkgroup |
+| Priority | | The priority field specifies the number of recorders the system must have available to record a new call for the talkgroup. For example, a priority of 1, the highest means as long as at least a single recorder is available, the system will record the new call. If the priority is 2, the system would at least 2 free recorders to record the new call, and so on. If there is no priority set for a talkgroup entry, a prioity of 1 is assumed.
Talkgroups assigned a priority of -1 will never be recorded, regardless of the number of available recorders. |
+| Preferred NAC | | In Multi-Site mode, the preferred NAC for a specific talk group is used to specify the site you prefer the talk group to be recorded from.|
+| Comment | | Use this field to capture comments about a talkgroup. It will be ignored by Trunk Recorder. |
-| DEC | HEX | Mode | Alpha Tag | Description | Tag | Group | Priority | PreferredNAC (optional) |
+Here are the column headers and some sample data:
+
+| Decimal | Hex | Mode | Alpha Tag | Description | Tag | Category | Priority | Preferred NAC |
|-----|-----|------|--------------|----------------|----------------|----------|----------|-------------------------|
|101 | 065 | D | DCFD 01 Disp | 01 Dispatch | Fire Dispatch | Fire | 1 | 1000 |
|2227 | 8b3 | D | DC StcarYard | Streetcar Yard | Transportation | Services | 3 | 1001 |
- In Multi-Site mode, the preferred NAC for a specific talk group is used to specify the site you prefer the talk group to be recorded from.
## channelFile
@@ -434,12 +446,25 @@ This file allows for you to specify additional information about conventional ch
*Tone based squelch is currently not supported.*
+
+| Column Name | Required | Value |
+|-------------|----------|-------|
+| TG Number | ✔️ | The Talkgroup Number formatted as a decimal number. This has to be the first column |
+| Frequency | ✔️ | The frequency in Hz for the channel |
+| Tone | ✔️ | The Tone for the talkgroup. This value is not used. *Tone based squelch is currently not supported.* |
+| Alpha Tag | | A 16 character description that is intended as a shortened display on radio displays |
+| Category | | The category for the Talkgroup |
+| Tag | | The Service Tag for the Talkgroup |
+| Comment | | Use this field to capture comments about a talkgroup. It will be ignored by Trunk Recorder. |
+| Enable | | Set to 'false' if you do not want this talkgroup/channel to created |
+
+
The **Enable** Column is optional and defaults to *True*. It only needs to be added to rows that you do not want to have recorded. For those rows, set **Enable** to *False*.
-| TG Number | Frequency | Tone | Alpha Tag | Description | Tag | Group | Enable (*optional*) |
+| TG Number | Frequency | Tone | Alpha Tag | Description | Tag | Category | Enable (*optional*) |
| --------- | --------- | -------- | ------------- | ---------------------- | ------ | ------ | ------------------- |
-| 300 | 462275000 | 94.8 PL | Town A Police | Town A Police Dispatch | Police | Town A | |
-| 325 | 462275000 | 151.4 PL | Town B DPW | Town B Trash Dispatch | DPW | Town B | False |
+| 300 | 462275000 | 94.8 | Town A Police | Town A Police Dispatch | Police | Town A | |
+| 325 | 462275000 | 151.4 | Town B DPW | Town B Trash Dispatch | DPW | Town B | False |
## unitTagsFile
diff --git a/lib/csv-parser/csv.hpp b/lib/csv-parser/csv.hpp
new file mode 100644
index 000000000..9cebfc2b0
--- /dev/null
+++ b/lib/csv-parser/csv.hpp
@@ -0,0 +1,8486 @@
+#pragma once
+/*
+CSV for C++, version 2.1.3
+https://github.com/vincentlaucsb/csv-parser
+
+MIT License
+
+Copyright (c) 2017-2020 Vincent La
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef CSV_HPP
+#define CSV_HPP
+
+/** @file
+ * @brief Defines functionality needed for basic CSV parsing
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Copyright 2017 https://github.com/mandreyel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MIO_MMAP_HEADER
+#define MIO_MMAP_HEADER
+
+// #include "mio/page.hpp"
+/* Copyright 2017 https://github.com/mandreyel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MIO_PAGE_HEADER
+#define MIO_PAGE_HEADER
+
+#ifdef _WIN32
+# include
+#else
+# include
+#endif
+
+namespace mio {
+
+/**
+ * This is used by `basic_mmap` to determine whether to create a read-only or
+ * a read-write memory mapping.
+ */
+enum class access_mode
+{
+ read,
+ write
+};
+
+/**
+ * Determines the operating system's page allocation granularity.
+ *
+ * On the first call to this function, it invokes the operating system specific syscall
+ * to determine the page size, caches the value, and returns it. Any subsequent call to
+ * this function serves the cached value, so no further syscalls are made.
+ */
+inline size_t page_size()
+{
+ static const size_t page_size = []
+ {
+#ifdef _WIN32
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ return SystemInfo.dwAllocationGranularity;
+#else
+ return sysconf(_SC_PAGE_SIZE);
+#endif
+ }();
+ return page_size;
+}
+
+/**
+ * Alligns `offset` to the operating's system page size such that it subtracts the
+ * difference until the nearest page boundary before `offset`, or does nothing if
+ * `offset` is already page aligned.
+ */
+inline size_t make_offset_page_aligned(size_t offset) noexcept
+{
+ const size_t page_size_ = page_size();
+ // Use integer division to round down to the nearest page alignment.
+ return offset / page_size_ * page_size_;
+}
+
+} // namespace mio
+
+#endif // MIO_PAGE_HEADER
+
+
+#include
+#include
+#include
+#include
+
+#ifdef _WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif // WIN32_LEAN_AND_MEAN
+# include
+#else // ifdef _WIN32
+# define INVALID_HANDLE_VALUE -1
+#endif // ifdef _WIN32
+
+namespace mio {
+
+// This value may be provided as the `length` parameter to the constructor or
+// `map`, in which case a memory mapping of the entire file is created.
+enum { map_entire_file = 0 };
+
+#ifdef _WIN32
+using file_handle_type = HANDLE;
+#else
+using file_handle_type = int;
+#endif
+
+// This value represents an invalid file handle type. This can be used to
+// determine whether `basic_mmap::file_handle` is valid, for example.
+const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE;
+
+template
+struct basic_mmap
+{
+ using value_type = ByteT;
+ using size_type = size_t;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = value_type*;
+ using const_pointer = const value_type*;
+ using difference_type = std::ptrdiff_t;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator;
+ using const_reverse_iterator = std::reverse_iterator;
+ using iterator_category = std::random_access_iterator_tag;
+ using handle_type = file_handle_type;
+
+ static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char.");
+
+private:
+ // Points to the first requested byte, and not to the actual start of the mapping.
+ pointer data_ = nullptr;
+
+ // Length--in bytes--requested by user (which may not be the length of the
+ // full mapping) and the length of the full mapping.
+ size_type length_ = 0;
+ size_type mapped_length_ = 0;
+
+ // Letting user map a file using both an existing file handle and a path
+ // introcudes some complexity (see `is_handle_internal_`).
+ // On POSIX, we only need a file handle to create a mapping, while on
+ // Windows systems the file handle is necessary to retrieve a file mapping
+ // handle, but any subsequent operations on the mapped region must be done
+ // through the latter.
+ handle_type file_handle_ = INVALID_HANDLE_VALUE;
+#ifdef _WIN32
+ handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
+#endif
+
+ // Letting user map a file using both an existing file handle and a path
+ // introcudes some complexity in that we must not close the file handle if
+ // user provided it, but we must close it if we obtained it using the
+ // provided path. For this reason, this flag is used to determine when to
+ // close `file_handle_`.
+ bool is_handle_internal_;
+
+public:
+ /**
+ * The default constructed mmap object is in a non-mapped state, that is,
+ * any operation that attempts to access nonexistent underlying data will
+ * result in undefined behaviour/segmentation faults.
+ */
+ basic_mmap() = default;
+
+#ifdef __cpp_exceptions
+ /**
+ * The same as invoking the `map` function, except any error that may occur
+ * while establishing the mapping is wrapped in a `std::system_error` and is
+ * thrown.
+ */
+ template
+ basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
+ {
+ std::error_code error;
+ map(path, offset, length, error);
+ if(error) { throw std::system_error(error); }
+ }
+
+ /**
+ * The same as invoking the `map` function, except any error that may occur
+ * while establishing the mapping is wrapped in a `std::system_error` and is
+ * thrown.
+ */
+ basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
+ {
+ std::error_code error;
+ map(handle, offset, length, error);
+ if(error) { throw std::system_error(error); }
+ }
+#endif // __cpp_exceptions
+
+ /**
+ * `basic_mmap` has single-ownership semantics, so transferring ownership
+ * may only be accomplished by moving the object.
+ */
+ basic_mmap(const basic_mmap&) = delete;
+ basic_mmap(basic_mmap&&);
+ basic_mmap& operator=(const basic_mmap&) = delete;
+ basic_mmap& operator=(basic_mmap&&);
+
+ /**
+ * If this is a read-write mapping, the destructor invokes sync. Regardless
+ * of the access mode, unmap is invoked as a final step.
+ */
+ ~basic_mmap();
+
+ /**
+ * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
+ * however, a mapped region of a file gets its own handle, which is returned by
+ * 'mapping_handle'.
+ */
+ handle_type file_handle() const noexcept { return file_handle_; }
+ handle_type mapping_handle() const noexcept;
+
+ /** Returns whether a valid memory mapping has been created. */
+ bool is_open() const noexcept { return file_handle_ != invalid_handle; }
+
+ /**
+ * Returns true if no mapping was established, that is, conceptually the
+ * same as though the length that was mapped was 0. This function is
+ * provided so that this class has Container semantics.
+ */
+ bool empty() const noexcept { return length() == 0; }
+
+ /** Returns true if a mapping was established. */
+ bool is_mapped() const noexcept;
+
+ /**
+ * `size` and `length` both return the logical length, i.e. the number of bytes
+ * user requested to be mapped, while `mapped_length` returns the actual number of
+ * bytes that were mapped which is a multiple of the underlying operating system's
+ * page allocation granularity.
+ */
+ size_type size() const noexcept { return length(); }
+ size_type length() const noexcept { return length_; }
+ size_type mapped_length() const noexcept { return mapped_length_; }
+
+ /** Returns the offset relative to the start of the mapping. */
+ size_type mapping_offset() const noexcept
+ {
+ return mapped_length_ - length_;
+ }
+
+ /**
+ * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
+ * exists.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > pointer data() noexcept { return data_; }
+ const_pointer data() const noexcept { return data_; }
+
+ /**
+ * Returns an iterator to the first requested byte, if a valid memory mapping
+ * exists, otherwise this function call is undefined behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > iterator begin() noexcept { return data(); }
+ const_iterator begin() const noexcept { return data(); }
+ const_iterator cbegin() const noexcept { return data(); }
+
+ /**
+ * Returns an iterator one past the last requested byte, if a valid memory mapping
+ * exists, otherwise this function call is undefined behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > iterator end() noexcept { return data() + length(); }
+ const_iterator end() const noexcept { return data() + length(); }
+ const_iterator cend() const noexcept { return data() + length(); }
+
+ /**
+ * Returns a reverse iterator to the last memory mapped byte, if a valid
+ * memory mapping exists, otherwise this function call is undefined
+ * behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
+ const_reverse_iterator rbegin() const noexcept
+ { return const_reverse_iterator(end()); }
+ const_reverse_iterator crbegin() const noexcept
+ { return const_reverse_iterator(end()); }
+
+ /**
+ * Returns a reverse iterator past the first mapped byte, if a valid memory
+ * mapping exists, otherwise this function call is undefined behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
+ const_reverse_iterator rend() const noexcept
+ { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crend() const noexcept
+ { return const_reverse_iterator(begin()); }
+
+ /**
+ * Returns a reference to the `i`th byte from the first requested byte (as returned
+ * by `data`). If this is invoked when no valid memory mapping has been created
+ * prior to this call, undefined behaviour ensues.
+ */
+ reference operator[](const size_type i) noexcept { return data_[i]; }
+ const_reference operator[](const size_type i) const noexcept { return data_[i]; }
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
+ * reason is reported via `error` and the object remains in a state as if this
+ * function hadn't been called.
+ *
+ * `path`, which must be a path to an existing file, is used to retrieve a file
+ * handle (which is closed when the object destructs or `unmap` is called), which is
+ * then used to memory map the requested region. Upon failure, `error` is set to
+ * indicate the reason and the object remains in an unmapped state.
+ *
+ * `offset` is the number of bytes, relative to the start of the file, where the
+ * mapping should begin. When specifying it, there is no need to worry about
+ * providing a value that is aligned with the operating system's page allocation
+ * granularity. This is adjusted by the implementation such that the first requested
+ * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
+ * `offset` from the start of the file.
+ *
+ * `length` is the number of bytes to map. It may be `map_entire_file`, in which
+ * case a mapping of the entire file is created.
+ */
+ template
+ void map(const String& path, const size_type offset,
+ const size_type length, std::error_code& error);
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
+ * reason is reported via `error` and the object remains in a state as if this
+ * function hadn't been called.
+ *
+ * `path`, which must be a path to an existing file, is used to retrieve a file
+ * handle (which is closed when the object destructs or `unmap` is called), which is
+ * then used to memory map the requested region. Upon failure, `error` is set to
+ * indicate the reason and the object remains in an unmapped state.
+ *
+ * The entire file is mapped.
+ */
+ template
+ void map(const String& path, std::error_code& error)
+ {
+ map(path, 0, map_entire_file, error);
+ }
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is
+ * unsuccesful, the reason is reported via `error` and the object remains in
+ * a state as if this function hadn't been called.
+ *
+ * `handle`, which must be a valid file handle, which is used to memory map the
+ * requested region. Upon failure, `error` is set to indicate the reason and the
+ * object remains in an unmapped state.
+ *
+ * `offset` is the number of bytes, relative to the start of the file, where the
+ * mapping should begin. When specifying it, there is no need to worry about
+ * providing a value that is aligned with the operating system's page allocation
+ * granularity. This is adjusted by the implementation such that the first requested
+ * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
+ * `offset` from the start of the file.
+ *
+ * `length` is the number of bytes to map. It may be `map_entire_file`, in which
+ * case a mapping of the entire file is created.
+ */
+ void map(const handle_type handle, const size_type offset,
+ const size_type length, std::error_code& error);
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is
+ * unsuccesful, the reason is reported via `error` and the object remains in
+ * a state as if this function hadn't been called.
+ *
+ * `handle`, which must be a valid file handle, which is used to memory map the
+ * requested region. Upon failure, `error` is set to indicate the reason and the
+ * object remains in an unmapped state.
+ *
+ * The entire file is mapped.
+ */
+ void map(const handle_type handle, std::error_code& error)
+ {
+ map(handle, 0, map_entire_file, error);
+ }
+
+ /**
+ * If a valid memory mapping has been created prior to this call, this call
+ * instructs the kernel to unmap the memory region and disassociate this object
+ * from the file.
+ *
+ * The file handle associated with the file that is mapped is only closed if the
+ * mapping was created using a file path. If, on the other hand, an existing
+ * file handle was used to create the mapping, the file handle is not closed.
+ */
+ void unmap();
+
+ void swap(basic_mmap& other);
+
+ /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
+ template
+ typename std::enable_if::type
+ sync(std::error_code& error);
+
+ /**
+ * All operators compare the address of the first byte and size of the two mapped
+ * regions.
+ */
+
+private:
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > pointer get_mapping_start() noexcept
+ {
+ return !data() ? nullptr : data() - mapping_offset();
+ }
+
+ const_pointer get_mapping_start() const noexcept
+ {
+ return !data() ? nullptr : data() - mapping_offset();
+ }
+
+ /**
+ * The destructor syncs changes to disk if `AccessMode` is `write`, but not
+ * if it's `read`, but since the destructor cannot be templated, we need to
+ * do SFINAE in a dedicated function, where one syncs and the other is a noop.
+ */
+ template
+ typename std::enable_if::type
+ conditional_sync();
+ template
+ typename std::enable_if::type conditional_sync();
+};
+
+template
+bool operator==(const basic_mmap& a,
+ const basic_mmap& b);
+
+template
+bool operator!=(const basic_mmap& a,
+ const basic_mmap& b);
+
+template
+bool operator<(const basic_mmap& a,
+ const basic_mmap& b);
+
+template
+bool operator<=(const basic_mmap& a,
+ const basic_mmap& b);
+
+template
+bool operator>(const basic_mmap& a,
+ const basic_mmap& b);
+
+template
+bool operator>=(const basic_mmap& a,
+ const basic_mmap& b);
+
+/**
+ * This is the basis for all read-only mmap objects and should be preferred over
+ * directly using `basic_mmap`.
+ */
+template
+using basic_mmap_source = basic_mmap;
+
+/**
+ * This is the basis for all read-write mmap objects and should be preferred over
+ * directly using `basic_mmap`.
+ */
+template
+using basic_mmap_sink = basic_mmap;
+
+/**
+ * These aliases cover the most common use cases, both representing a raw byte stream
+ * (either with a char or an unsigned char/uint8_t).
+ */
+using mmap_source = basic_mmap_source;
+using ummap_source = basic_mmap_source;
+
+using mmap_sink = basic_mmap_sink;
+using ummap_sink = basic_mmap_sink;
+
+/**
+ * Convenience factory method that constructs a mapping for any `basic_mmap` or
+ * `basic_mmap` type.
+ */
+template<
+ typename MMap,
+ typename MappingToken
+> MMap make_mmap(const MappingToken& token,
+ int64_t offset, int64_t length, std::error_code& error)
+{
+ MMap mmap;
+ mmap.map(token, offset, length, error);
+ return mmap;
+}
+
+/**
+ * Convenience factory method.
+ *
+ * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
+ * `std::filesystem::path`, `std::vector`, or similar), or a
+ * `mmap_source::handle_type`.
+ */
+template
+mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
+ mmap_source::size_type length, std::error_code& error)
+{
+ return make_mmap(token, offset, length, error);
+}
+
+template
+mmap_source make_mmap_source(const MappingToken& token, std::error_code& error)
+{
+ return make_mmap_source(token, 0, map_entire_file, error);
+}
+
+/**
+ * Convenience factory method.
+ *
+ * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
+ * `std::filesystem::path`, `std::vector`, or similar), or a
+ * `mmap_sink::handle_type`.
+ */
+template
+mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
+ mmap_sink::size_type length, std::error_code& error)
+{
+ return make_mmap(token, offset, length, error);
+}
+
+template
+mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error)
+{
+ return make_mmap_sink(token, 0, map_entire_file, error);
+}
+
+} // namespace mio
+
+// #include "detail/mmap.ipp"
+/* Copyright 2017 https://github.com/mandreyel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MIO_BASIC_MMAP_IMPL
+#define MIO_BASIC_MMAP_IMPL
+
+// #include "mio/mmap.hpp"
+
+// #include "mio/page.hpp"
+
+// #include "mio/detail/string_util.hpp"
+/* Copyright 2017 https://github.com/mandreyel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MIO_STRING_UTIL_HEADER
+#define MIO_STRING_UTIL_HEADER
+
+#include
+
+namespace mio {
+namespace detail {
+
+template<
+ typename S,
+ typename C = typename std::decay::type,
+ typename = decltype(std::declval().data()),
+ typename = typename std::enable_if<
+ std::is_same::value
+#ifdef _WIN32
+ || std::is_same::value
+#endif
+ >::type
+> struct char_type_helper {
+ using type = typename C::value_type;
+};
+
+template
+struct char_type {
+ using type = typename char_type_helper::type;
+};
+
+// TODO: can we avoid this brute force approach?
+template<>
+struct char_type {
+ using type = char;
+};
+
+template<>
+struct char_type {
+ using type = char;
+};
+
+template
+struct char_type {
+ using type = char;
+};
+
+template
+struct char_type {
+ using type = char;
+};
+
+#ifdef _WIN32
+template<>
+struct char_type {
+ using type = wchar_t;
+};
+
+template<>
+struct char_type {
+ using type = wchar_t;
+};
+
+template
+struct char_type {
+ using type = wchar_t;
+};
+
+template
+struct char_type {
+ using type = wchar_t;
+};
+#endif // _WIN32
+
+template
+struct is_c_str_helper
+{
+ static constexpr bool value = std::is_same<
+ CharT*,
+ // TODO: I'm so sorry for this... Can this be made cleaner?
+ typename std::add_pointer<
+ typename std::remove_cv<
+ typename std::remove_pointer<
+ typename std::decay<
+ S
+ >::type
+ >::type
+ >::type
+ >::type
+ >::value;
+};
+
+template
+struct is_c_str
+{
+ static constexpr bool value = is_c_str_helper::value;
+};
+
+#ifdef _WIN32
+template
+struct is_c_wstr
+{
+ static constexpr bool value = is_c_str_helper::value;
+};
+#endif // _WIN32
+
+template
+struct is_c_str_or_c_wstr
+{
+ static constexpr bool value = is_c_str::value
+#ifdef _WIN32
+ || is_c_wstr::value
+#endif
+ ;
+};
+
+template<
+ typename String,
+ typename = decltype(std::declval().data()),
+ typename = typename std::enable_if::value>::type
+> const typename char_type::type* c_str(const String& path)
+{
+ return path.data();
+}
+
+template<
+ typename String,
+ typename = decltype(std::declval().empty()),
+ typename = typename std::enable_if::value>::type
+> bool empty(const String& path)
+{
+ return path.empty();
+}
+
+template<
+ typename String,
+ typename = typename std::enable_if::value>::type
+> const typename char_type::type* c_str(String path)
+{
+ return path;
+}
+
+template<
+ typename String,
+ typename = typename std::enable_if::value>::type
+> bool empty(String path)
+{
+ return !path || (*path == 0);
+}
+
+} // namespace detail
+} // namespace mio
+
+#endif // MIO_STRING_UTIL_HEADER
+
+
+#include
+
+#ifndef _WIN32
+# include
+# include
+# include
+# include
+#endif
+
+namespace mio {
+namespace detail {
+
+#ifdef _WIN32
+namespace win {
+
+/** Returns the 4 upper bytes of an 8-byte integer. */
+inline DWORD int64_high(int64_t n) noexcept
+{
+ return n >> 32;
+}
+
+/** Returns the 4 lower bytes of an 8-byte integer. */
+inline DWORD int64_low(int64_t n) noexcept
+{
+ return n & 0xffffffff;
+}
+
+template<
+ typename String,
+ typename = typename std::enable_if<
+ std::is_same::type, char>::value
+ >::type
+> file_handle_type open_file_helper(const String& path, const access_mode mode)
+{
+ return ::CreateFileA(c_str(path),
+ mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+}
+
+template
+typename std::enable_if<
+ std::is_same::type, wchar_t>::value,
+ file_handle_type
+>::type open_file_helper(const String& path, const access_mode mode)
+{
+ return ::CreateFileW(c_str(path),
+ mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+}
+
+} // win
+#endif // _WIN32
+
+/**
+ * Returns the last platform specific system error (errno on POSIX and
+ * GetLastError on Win) as a `std::error_code`.
+ */
+inline std::error_code last_error() noexcept
+{
+ std::error_code error;
+#ifdef _WIN32
+ error.assign(GetLastError(), std::system_category());
+#else
+ error.assign(errno, std::system_category());
+#endif
+ return error;
+}
+
+template
+file_handle_type open_file(const String& path, const access_mode mode,
+ std::error_code& error)
+{
+ error.clear();
+ if(detail::empty(path))
+ {
+ error = std::make_error_code(std::errc::invalid_argument);
+ return invalid_handle;
+ }
+#ifdef _WIN32
+ const auto handle = win::open_file_helper(path, mode);
+#else // POSIX
+ const auto handle = ::open(c_str(path),
+ mode == access_mode::read ? O_RDONLY : O_RDWR);
+#endif
+ if(handle == invalid_handle)
+ {
+ error = detail::last_error();
+ }
+ return handle;
+}
+
+inline size_t query_file_size(file_handle_type handle, std::error_code& error)
+{
+ error.clear();
+#ifdef _WIN32
+ LARGE_INTEGER file_size;
+ if(::GetFileSizeEx(handle, &file_size) == 0)
+ {
+ error = detail::last_error();
+ return 0;
+ }
+ return static_cast(file_size.QuadPart);
+#else // POSIX
+ struct stat sbuf;
+ if(::fstat(handle, &sbuf) == -1)
+ {
+ error = detail::last_error();
+ return 0;
+ }
+ return sbuf.st_size;
+#endif
+}
+
+struct mmap_context
+{
+ char* data;
+ int64_t length;
+ int64_t mapped_length;
+#ifdef _WIN32
+ file_handle_type file_mapping_handle;
+#endif
+};
+
+inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset,
+ const int64_t length, const access_mode mode, std::error_code& error)
+{
+ const int64_t aligned_offset = make_offset_page_aligned(offset);
+ const int64_t length_to_map = offset - aligned_offset + length;
+#ifdef _WIN32
+ const int64_t max_file_size = offset + length;
+ const auto file_mapping_handle = ::CreateFileMapping(
+ file_handle,
+ 0,
+ mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
+ win::int64_high(max_file_size),
+ win::int64_low(max_file_size),
+ 0);
+ if(file_mapping_handle == invalid_handle)
+ {
+ error = detail::last_error();
+ return {};
+ }
+ char* mapping_start = static_cast(::MapViewOfFile(
+ file_mapping_handle,
+ mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
+ win::int64_high(aligned_offset),
+ win::int64_low(aligned_offset),
+ length_to_map));
+ if(mapping_start == nullptr)
+ {
+ // Close file handle if mapping it failed.
+ ::CloseHandle(file_mapping_handle);
+ error = detail::last_error();
+ return {};
+ }
+#else // POSIX
+ char* mapping_start = static_cast(::mmap(
+ 0, // Don't give hint as to where to map.
+ length_to_map,
+ mode == access_mode::read ? PROT_READ : PROT_WRITE,
+ MAP_SHARED,
+ file_handle,
+ aligned_offset));
+ if(mapping_start == MAP_FAILED)
+ {
+ error = detail::last_error();
+ return {};
+ }
+#endif
+ mmap_context ctx;
+ ctx.data = mapping_start + offset - aligned_offset;
+ ctx.length = length;
+ ctx.mapped_length = length_to_map;
+#ifdef _WIN32
+ ctx.file_mapping_handle = file_mapping_handle;
+#endif
+ return ctx;
+}
+
+} // namespace detail
+
+// -- basic_mmap --
+
+template
+basic_mmap::~basic_mmap()
+{
+ conditional_sync();
+ unmap();
+}
+
+template
+basic_mmap::basic_mmap(basic_mmap&& other)
+ : data_(std::move(other.data_))
+ , length_(std::move(other.length_))
+ , mapped_length_(std::move(other.mapped_length_))
+ , file_handle_(std::move(other.file_handle_))
+#ifdef _WIN32
+ , file_mapping_handle_(std::move(other.file_mapping_handle_))
+#endif
+ , is_handle_internal_(std::move(other.is_handle_internal_))
+{
+ other.data_ = nullptr;
+ other.length_ = other.mapped_length_ = 0;
+ other.file_handle_ = invalid_handle;
+#ifdef _WIN32
+ other.file_mapping_handle_ = invalid_handle;
+#endif
+}
+
+template
+basic_mmap&
+basic_mmap::operator=(basic_mmap&& other)
+{
+ if(this != &other)
+ {
+ // First the existing mapping needs to be removed.
+ unmap();
+ data_ = std::move(other.data_);
+ length_ = std::move(other.length_);
+ mapped_length_ = std::move(other.mapped_length_);
+ file_handle_ = std::move(other.file_handle_);
+#ifdef _WIN32
+ file_mapping_handle_ = std::move(other.file_mapping_handle_);
+#endif
+ is_handle_internal_ = std::move(other.is_handle_internal_);
+
+ // The moved from basic_mmap's fields need to be reset, because
+ // otherwise other's destructor will unmap the same mapping that was
+ // just moved into this.
+ other.data_ = nullptr;
+ other.length_ = other.mapped_length_ = 0;
+ other.file_handle_ = invalid_handle;
+#ifdef _WIN32
+ other.file_mapping_handle_ = invalid_handle;
+#endif
+ other.is_handle_internal_ = false;
+ }
+ return *this;
+}
+
+template
+typename basic_mmap::handle_type
+basic_mmap::mapping_handle() const noexcept
+{
+#ifdef _WIN32
+ return file_mapping_handle_;
+#else
+ return file_handle_;
+#endif
+}
+
+template
+template
+void basic_mmap::map(const String& path, const size_type offset,
+ const size_type length, std::error_code& error)
+{
+ error.clear();
+ if(detail::empty(path))
+ {
+ error = std::make_error_code(std::errc::invalid_argument);
+ return;
+ }
+ const auto handle = detail::open_file(path, AccessMode, error);
+ if(error)
+ {
+ return;
+ }
+
+ map(handle, offset, length, error);
+ // This MUST be after the call to map, as that sets this to true.
+ if(!error)
+ {
+ is_handle_internal_ = true;
+ }
+}
+
+template
+void basic_mmap::map(const handle_type handle,
+ const size_type offset, const size_type length, std::error_code& error)
+{
+ error.clear();
+ if(handle == invalid_handle)
+ {
+ error = std::make_error_code(std::errc::bad_file_descriptor);
+ return;
+ }
+
+ const auto file_size = detail::query_file_size(handle, error);
+ if(error)
+ {
+ return;
+ }
+
+ if(offset + length > file_size)
+ {
+ error = std::make_error_code(std::errc::invalid_argument);
+ return;
+ }
+
+ const auto ctx = detail::memory_map(handle, offset,
+ length == map_entire_file ? (file_size - offset) : length,
+ AccessMode, error);
+ if(!error)
+ {
+ // We must unmap the previous mapping that may have existed prior to this call.
+ // Note that this must only be invoked after a new mapping has been created in
+ // order to provide the strong guarantee that, should the new mapping fail, the
+ // `map` function leaves this instance in a state as though the function had
+ // never been invoked.
+ unmap();
+ file_handle_ = handle;
+ is_handle_internal_ = false;
+ data_ = reinterpret_cast(ctx.data);
+ length_ = ctx.length;
+ mapped_length_ = ctx.mapped_length;
+#ifdef _WIN32
+ file_mapping_handle_ = ctx.file_mapping_handle;
+#endif
+ }
+}
+
+template
+template
+typename std::enable_if::type
+basic_mmap::sync(std::error_code& error)
+{
+ error.clear();
+ if(!is_open())
+ {
+ error = std::make_error_code(std::errc::bad_file_descriptor);
+ return;
+ }
+
+ if(data())
+ {
+#ifdef _WIN32
+ if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
+ || ::FlushFileBuffers(file_handle_) == 0)
+#else // POSIX
+ if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
+#endif
+ {
+ error = detail::last_error();
+ return;
+ }
+ }
+#ifdef _WIN32
+ if(::FlushFileBuffers(file_handle_) == 0)
+ {
+ error = detail::last_error();
+ }
+#endif
+}
+
+template
+void basic_mmap::unmap()
+{
+ if(!is_open()) { return; }
+ // TODO do we care about errors here?
+#ifdef _WIN32
+ if(is_mapped())
+ {
+ ::UnmapViewOfFile(get_mapping_start());
+ ::CloseHandle(file_mapping_handle_);
+ }
+#else // POSIX
+ if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); }
+#endif
+
+ // If `file_handle_` was obtained by our opening it (when map is called with
+ // a path, rather than an existing file handle), we need to close it,
+ // otherwise it must not be closed as it may still be used outside this
+ // instance.
+ if(is_handle_internal_)
+ {
+#ifdef _WIN32
+ ::CloseHandle(file_handle_);
+#else // POSIX
+ ::close(file_handle_);
+#endif
+ }
+
+ // Reset fields to their default values.
+ data_ = nullptr;
+ length_ = mapped_length_ = 0;
+ file_handle_ = invalid_handle;
+#ifdef _WIN32
+ file_mapping_handle_ = invalid_handle;
+#endif
+}
+
+template
+bool basic_mmap::is_mapped() const noexcept
+{
+#ifdef _WIN32
+ return file_mapping_handle_ != invalid_handle;
+#else // POSIX
+ return is_open();
+#endif
+}
+
+template
+void basic_mmap::swap(basic_mmap& other)
+{
+ if(this != &other)
+ {
+ using std::swap;
+ swap(data_, other.data_);
+ swap(file_handle_, other.file_handle_);
+#ifdef _WIN32
+ swap(file_mapping_handle_, other.file_mapping_handle_);
+#endif
+ swap(length_, other.length_);
+ swap(mapped_length_, other.mapped_length_);
+ swap(is_handle_internal_, other.is_handle_internal_);
+ }
+}
+
+template
+template
+typename std::enable_if::type
+basic_mmap::conditional_sync()
+{
+ // This is invoked from the destructor, so not much we can do about
+ // failures here.
+ std::error_code ec;
+ sync(ec);
+}
+
+template
+template
+typename std::enable_if::type
+basic_mmap::conditional_sync()
+{
+ // noop
+}
+
+template
+bool operator==(const basic_mmap& a,
+ const basic_mmap& b)
+{
+ return a.data() == b.data()
+ && a.size() == b.size();
+}
+
+template
+bool operator!=(const basic_mmap& a,
+ const basic_mmap& b)
+{
+ return !(a == b);
+}
+
+template
+bool operator<(const basic_mmap& a,
+ const basic_mmap& b)
+{
+ if(a.data() == b.data()) { return a.size() < b.size(); }
+ return a.data() < b.data();
+}
+
+template
+bool operator<=(const basic_mmap& a,
+ const basic_mmap& b)
+{
+ return !(a > b);
+}
+
+template
+bool operator>(const basic_mmap& a,
+ const basic_mmap& b)
+{
+ if(a.data() == b.data()) { return a.size() > b.size(); }
+ return a.data() > b.data();
+}
+
+template
+bool operator>=(const basic_mmap& a,
+ const basic_mmap& b)
+{
+ return !(a < b);
+}
+
+} // namespace mio
+
+#endif // MIO_BASIC_MMAP_IMPL
+
+
+#endif // MIO_MMAP_HEADER
+/* Copyright 2017 https://github.com/mandreyel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MIO_PAGE_HEADER
+#define MIO_PAGE_HEADER
+
+#ifdef _WIN32
+# include
+#else
+# include
+#endif
+
+namespace mio {
+
+/**
+ * This is used by `basic_mmap` to determine whether to create a read-only or
+ * a read-write memory mapping.
+ */
+enum class access_mode
+{
+ read,
+ write
+};
+
+/**
+ * Determines the operating system's page allocation granularity.
+ *
+ * On the first call to this function, it invokes the operating system specific syscall
+ * to determine the page size, caches the value, and returns it. Any subsequent call to
+ * this function serves the cached value, so no further syscalls are made.
+ */
+inline size_t page_size()
+{
+ static const size_t page_size = []
+ {
+#ifdef _WIN32
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ return SystemInfo.dwAllocationGranularity;
+#else
+ return sysconf(_SC_PAGE_SIZE);
+#endif
+ }();
+ return page_size;
+}
+
+/**
+ * Alligns `offset` to the operating's system page size such that it subtracts the
+ * difference until the nearest page boundary before `offset`, or does nothing if
+ * `offset` is already page aligned.
+ */
+inline size_t make_offset_page_aligned(size_t offset) noexcept
+{
+ const size_t page_size_ = page_size();
+ // Use integer division to round down to the nearest page alignment.
+ return offset / page_size_ * page_size_;
+}
+
+} // namespace mio
+
+#endif // MIO_PAGE_HEADER
+/* Copyright 2017 https://github.com/mandreyel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MIO_SHARED_MMAP_HEADER
+#define MIO_SHARED_MMAP_HEADER
+
+// #include "mio/mmap.hpp"
+
+
+#include // std::error_code
+#include // std::shared_ptr
+
+namespace mio {
+
+/**
+ * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with
+ * `std::shared_ptr` semantics.
+ *
+ * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if
+ * shared semantics are not required.
+ */
+template<
+ access_mode AccessMode,
+ typename ByteT
+> class basic_shared_mmap
+{
+ using impl_type = basic_mmap;
+ std::shared_ptr pimpl_;
+
+public:
+ using value_type = typename impl_type::value_type;
+ using size_type = typename impl_type::size_type;
+ using reference = typename impl_type::reference;
+ using const_reference = typename impl_type::const_reference;
+ using pointer = typename impl_type::pointer;
+ using const_pointer = typename impl_type::const_pointer;
+ using difference_type = typename impl_type::difference_type;
+ using iterator = typename impl_type::iterator;
+ using const_iterator = typename impl_type::const_iterator;
+ using reverse_iterator = typename impl_type::reverse_iterator;
+ using const_reverse_iterator = typename impl_type::const_reverse_iterator;
+ using iterator_category = typename impl_type::iterator_category;
+ using handle_type = typename impl_type::handle_type;
+ using mmap_type = impl_type;
+
+ basic_shared_mmap() = default;
+ basic_shared_mmap(const basic_shared_mmap&) = default;
+ basic_shared_mmap& operator=(const basic_shared_mmap&) = default;
+ basic_shared_mmap(basic_shared_mmap&&) = default;
+ basic_shared_mmap& operator=(basic_shared_mmap&&) = default;
+
+ /** Takes ownership of an existing mmap object. */
+ basic_shared_mmap(mmap_type&& mmap)
+ : pimpl_(std::make_shared(std::move(mmap)))
+ {}
+
+ /** Takes ownership of an existing mmap object. */
+ basic_shared_mmap& operator=(mmap_type&& mmap)
+ {
+ pimpl_ = std::make_shared(std::move(mmap));
+ return *this;
+ }
+
+ /** Initializes this object with an already established shared mmap. */
+ basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {}
+
+ /** Initializes this object with an already established shared mmap. */
+ basic_shared_mmap& operator=(std::shared_ptr mmap)
+ {
+ pimpl_ = std::move(mmap);
+ return *this;
+ }
+
+#ifdef __cpp_exceptions
+ /**
+ * The same as invoking the `map` function, except any error that may occur
+ * while establishing the mapping is wrapped in a `std::system_error` and is
+ * thrown.
+ */
+ template
+ basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
+ {
+ std::error_code error;
+ map(path, offset, length, error);
+ if(error) { throw std::system_error(error); }
+ }
+
+ /**
+ * The same as invoking the `map` function, except any error that may occur
+ * while establishing the mapping is wrapped in a `std::system_error` and is
+ * thrown.
+ */
+ basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
+ {
+ std::error_code error;
+ map(handle, offset, length, error);
+ if(error) { throw std::system_error(error); }
+ }
+#endif // __cpp_exceptions
+
+ /**
+ * If this is a read-write mapping and the last reference to the mapping,
+ * the destructor invokes sync. Regardless of the access mode, unmap is
+ * invoked as a final step.
+ */
+ ~basic_shared_mmap() = default;
+
+ /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */
+ std::shared_ptr get_shared_ptr() { return pimpl_; }
+
+ /**
+ * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
+ * however, a mapped region of a file gets its own handle, which is returned by
+ * 'mapping_handle'.
+ */
+ handle_type file_handle() const noexcept
+ {
+ return pimpl_ ? pimpl_->file_handle() : invalid_handle;
+ }
+
+ handle_type mapping_handle() const noexcept
+ {
+ return pimpl_ ? pimpl_->mapping_handle() : invalid_handle;
+ }
+
+ /** Returns whether a valid memory mapping has been created. */
+ bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); }
+
+ /**
+ * Returns true if no mapping was established, that is, conceptually the
+ * same as though the length that was mapped was 0. This function is
+ * provided so that this class has Container semantics.
+ */
+ bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); }
+
+ /**
+ * `size` and `length` both return the logical length, i.e. the number of bytes
+ * user requested to be mapped, while `mapped_length` returns the actual number of
+ * bytes that were mapped which is a multiple of the underlying operating system's
+ * page allocation granularity.
+ */
+ size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
+ size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
+ size_type mapped_length() const noexcept
+ {
+ return pimpl_ ? pimpl_->mapped_length() : 0;
+ }
+
+ /**
+ * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
+ * exists.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > pointer data() noexcept { return pimpl_->data(); }
+ const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; }
+
+ /**
+ * Returns an iterator to the first requested byte, if a valid memory mapping
+ * exists, otherwise this function call is undefined behaviour.
+ */
+ iterator begin() noexcept { return pimpl_->begin(); }
+ const_iterator begin() const noexcept { return pimpl_->begin(); }
+ const_iterator cbegin() const noexcept { return pimpl_->cbegin(); }
+
+ /**
+ * Returns an iterator one past the last requested byte, if a valid memory mapping
+ * exists, otherwise this function call is undefined behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > iterator end() noexcept { return pimpl_->end(); }
+ const_iterator end() const noexcept { return pimpl_->end(); }
+ const_iterator cend() const noexcept { return pimpl_->cend(); }
+
+ /**
+ * Returns a reverse iterator to the last memory mapped byte, if a valid
+ * memory mapping exists, otherwise this function call is undefined
+ * behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); }
+ const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); }
+ const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); }
+
+ /**
+ * Returns a reverse iterator past the first mapped byte, if a valid memory
+ * mapping exists, otherwise this function call is undefined behaviour.
+ */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > reverse_iterator rend() noexcept { return pimpl_->rend(); }
+ const_reverse_iterator rend() const noexcept { return pimpl_->rend(); }
+ const_reverse_iterator crend() const noexcept { return pimpl_->crend(); }
+
+ /**
+ * Returns a reference to the `i`th byte from the first requested byte (as returned
+ * by `data`). If this is invoked when no valid memory mapping has been created
+ * prior to this call, undefined behaviour ensues.
+ */
+ reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; }
+ const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; }
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
+ * reason is reported via `error` and the object remains in a state as if this
+ * function hadn't been called.
+ *
+ * `path`, which must be a path to an existing file, is used to retrieve a file
+ * handle (which is closed when the object destructs or `unmap` is called), which is
+ * then used to memory map the requested region. Upon failure, `error` is set to
+ * indicate the reason and the object remains in an unmapped state.
+ *
+ * `offset` is the number of bytes, relative to the start of the file, where the
+ * mapping should begin. When specifying it, there is no need to worry about
+ * providing a value that is aligned with the operating system's page allocation
+ * granularity. This is adjusted by the implementation such that the first requested
+ * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
+ * `offset` from the start of the file.
+ *
+ * `length` is the number of bytes to map. It may be `map_entire_file`, in which
+ * case a mapping of the entire file is created.
+ */
+ template
+ void map(const String& path, const size_type offset,
+ const size_type length, std::error_code& error)
+ {
+ map_impl(path, offset, length, error);
+ }
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
+ * reason is reported via `error` and the object remains in a state as if this
+ * function hadn't been called.
+ *
+ * `path`, which must be a path to an existing file, is used to retrieve a file
+ * handle (which is closed when the object destructs or `unmap` is called), which is
+ * then used to memory map the requested region. Upon failure, `error` is set to
+ * indicate the reason and the object remains in an unmapped state.
+ *
+ * The entire file is mapped.
+ */
+ template
+ void map(const String& path, std::error_code& error)
+ {
+ map_impl(path, 0, map_entire_file, error);
+ }
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
+ * reason is reported via `error` and the object remains in a state as if this
+ * function hadn't been called.
+ *
+ * `handle`, which must be a valid file handle, which is used to memory map the
+ * requested region. Upon failure, `error` is set to indicate the reason and the
+ * object remains in an unmapped state.
+ *
+ * `offset` is the number of bytes, relative to the start of the file, where the
+ * mapping should begin. When specifying it, there is no need to worry about
+ * providing a value that is aligned with the operating system's page allocation
+ * granularity. This is adjusted by the implementation such that the first requested
+ * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
+ * `offset` from the start of the file.
+ *
+ * `length` is the number of bytes to map. It may be `map_entire_file`, in which
+ * case a mapping of the entire file is created.
+ */
+ void map(const handle_type handle, const size_type offset,
+ const size_type length, std::error_code& error)
+ {
+ map_impl(handle, offset, length, error);
+ }
+
+ /**
+ * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
+ * reason is reported via `error` and the object remains in a state as if this
+ * function hadn't been called.
+ *
+ * `handle`, which must be a valid file handle, which is used to memory map the
+ * requested region. Upon failure, `error` is set to indicate the reason and the
+ * object remains in an unmapped state.
+ *
+ * The entire file is mapped.
+ */
+ void map(const handle_type handle, std::error_code& error)
+ {
+ map_impl(handle, 0, map_entire_file, error);
+ }
+
+ /**
+ * If a valid memory mapping has been created prior to this call, this call
+ * instructs the kernel to unmap the memory region and disassociate this object
+ * from the file.
+ *
+ * The file handle associated with the file that is mapped is only closed if the
+ * mapping was created using a file path. If, on the other hand, an existing
+ * file handle was used to create the mapping, the file handle is not closed.
+ */
+ void unmap() { if(pimpl_) pimpl_->unmap(); }
+
+ void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); }
+
+ /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
+ template<
+ access_mode A = AccessMode,
+ typename = typename std::enable_if::type
+ > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); }
+
+ /** All operators compare the underlying `basic_mmap`'s addresses. */
+
+ friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b)
+ {
+ return a.pimpl_ == b.pimpl_;
+ }
+
+ friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b)
+ {
+ return !(a == b);
+ }
+
+ friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b)
+ {
+ return a.pimpl_ < b.pimpl_;
+ }
+
+ friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b)
+ {
+ return a.pimpl_ <= b.pimpl_;
+ }
+
+ friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b)
+ {
+ return a.pimpl_ > b.pimpl_;
+ }
+
+ friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b)
+ {
+ return a.pimpl_ >= b.pimpl_;
+ }
+
+private:
+ template
+ void map_impl(const MappingToken& token, const size_type offset,
+ const size_type length, std::error_code& error)
+ {
+ if(!pimpl_)
+ {
+ mmap_type mmap = make_mmap(token, offset, length, error);
+ if(error) { return; }
+ pimpl_ = std::make_shared(std::move(mmap));
+ }
+ else
+ {
+ pimpl_->map(token, offset, length, error);
+ }
+ }
+};
+
+/**
+ * This is the basis for all read-only mmap objects and should be preferred over
+ * directly using basic_shared_mmap.
+ */
+template
+using basic_shared_mmap_source = basic_shared_mmap;
+
+/**
+ * This is the basis for all read-write mmap objects and should be preferred over
+ * directly using basic_shared_mmap.
+ */
+template
+using basic_shared_mmap_sink = basic_shared_mmap;
+
+/**
+ * These aliases cover the most common use cases, both representing a raw byte stream
+ * (either with a char or an unsigned char/uint8_t).
+ */
+using shared_mmap_source = basic_shared_mmap_source;
+using shared_ummap_source = basic_shared_mmap_source;
+
+using shared_mmap_sink = basic_shared_mmap_sink;
+using shared_ummap_sink = basic_shared_mmap_sink;
+
+} // namespace mio
+
+#endif // MIO_SHARED_MMAP_HEADER
+
+/** @file
+ * @brief Contains the main CSV parsing algorithm and various utility functions
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+/** @file
+ * A standalone header file containing shared code
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#if defined(_WIN32)
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include
+# undef max
+# undef min
+#elif defined(__linux__)
+# include
+#endif
+
+ /** Helper macro which should be #defined as "inline"
+ * in the single header version
+ */
+#define CSV_INLINE inline
+
+#include
+
+// Copyright 2017-2019 by Martin Moene
+//
+// string-view lite, a C++17-like string_view for C++98 and later.
+// For more information see https://github.com/martinmoene/string-view-lite
+//
+// 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_SV_LITE_H_INCLUDED
+#define NONSTD_SV_LITE_H_INCLUDED
+
+#define string_view_lite_MAJOR 1
+#define string_view_lite_MINOR 1
+#define string_view_lite_PATCH 0
+
+#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
+
+#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x )
+#define nssv_STRINGIFY_( x ) #x
+
+// string-view lite configuration:
+
+#define nssv_STRING_VIEW_DEFAULT 0
+#define nssv_STRING_VIEW_NONSTD 1
+#define nssv_STRING_VIEW_STD 2
+
+#if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
+# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
+#endif
+
+#if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW )
+# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
+#endif
+
+#ifndef nssv_CONFIG_STD_SV_OPERATOR
+# define nssv_CONFIG_STD_SV_OPERATOR 0
+#endif
+
+#ifndef nssv_CONFIG_USR_SV_OPERATOR
+# define nssv_CONFIG_USR_SV_OPERATOR 1
+#endif
+
+#ifdef nssv_CONFIG_CONVERSION_STD_STRING
+# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
+# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
+#endif
+
+#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
+#endif
+
+#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef nssv_CONFIG_NO_EXCEPTIONS
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+# define nssv_CONFIG_NO_EXCEPTIONS 0
+# else
+# define nssv_CONFIG_NO_EXCEPTIONS 1
+# endif
+#endif
+
+// C++ language version detection (C++20 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef nssv_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+# define nssv_CPLUSPLUS __cplusplus
+# endif
+#endif
+
+#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L )
+#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L )
+#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
+#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L )
+#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L )
+#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L )
+
+// use C++17 std::string_view if available and requested:
+
+#if nssv_CPP17_OR_GREATER && defined(__has_include )
+# if __has_include( )
+# define nssv_HAVE_STD_STRING_VIEW 1
+# else
+# define nssv_HAVE_STD_STRING_VIEW 0
+# endif
+#else
+# define nssv_HAVE_STD_STRING_VIEW 0
+#endif
+
+#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
+
+#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
+#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
+
+//
+// Use C++17 std::string_view:
+//
+
+#if nssv_USES_STD_STRING_VIEW
+
+#include
+
+// Extensions for std::string:
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+
+template< class CharT, class Traits, class Allocator = std::allocator >
+std::basic_string
+to_string( std::basic_string_view v, Allocator const & a = Allocator() )
+{
+ return std::basic_string( v.begin(), v.end(), a );
+}
+
+template< class CharT, class Traits, class Allocator >
+std::basic_string_view
+to_string_view( std::basic_string const & s )
+{
+ return std::basic_string_view( s.data(), s.size() );
+}
+
+// Literal operators sv and _sv:
+
+#if nssv_CONFIG_STD_SV_OPERATOR
+
+using namespace std::literals::string_view_literals;
+
+#endif
+
+#if nssv_CONFIG_USR_SV_OPERATOR
+
+inline namespace literals {
+inline namespace string_view_literals {
+
+
+constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1)
+{
+ return std::string_view{ str, len };
+}
+
+constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2)
+{
+ return std::u16string_view{ str, len };
+}
+
+constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3)
+{
+ return std::u32string_view{ str, len };
+}
+
+constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4)
+{
+ return std::wstring_view{ str, len };
+}
+
+}} // namespace literals::string_view_literals
+
+#endif // nssv_CONFIG_USR_SV_OPERATOR
+
+} // namespace nonstd
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+
+using std::string_view;
+using std::wstring_view;
+using std::u16string_view;
+using std::u32string_view;
+using std::basic_string_view;
+
+// literal "sv" and "_sv", see above
+
+using std::operator==;
+using std::operator!=;
+using std::operator<;
+using std::operator<=;
+using std::operator>;
+using std::operator>=;
+
+using std::operator<<;
+
+} // namespace nonstd
+
+#else // nssv_HAVE_STD_STRING_VIEW
+
+//
+// Before C++17: use string_view lite:
+//
+
+// Compiler versions:
+//
+// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
+// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
+// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
+// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
+// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define nssv_COMPILER_MSVC_VER (_MSC_VER )
+# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define nssv_COMPILER_MSVC_VER 0
+# define nssv_COMPILER_MSVC_VERSION 0
+#endif
+
+#define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch)
+
+#if defined(__clang__)
+# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define nssv_COMPILER_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define nssv_COMPILER_GNUC_VERSION 0
+#endif
+
+// half-open range [lo..hi):
+#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define nssv_HAS_CPP0X _HAS_CPP0X
+#else
+# define nssv_HAS_CPP0X 0
+#endif
+
+// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
+
+#if nssv_COMPILER_MSVC_VER >= 1900
+# undef nssv_CPP11_OR_GREATER
+# define nssv_CPP11_OR_GREATER 1
+#endif
+
+#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
+#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
+#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
+#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
+#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
+#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
+
+#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
+#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
+#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
+#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
+#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
+#define nssv_HAVE_NULLPTR nssv_CPP11_100
+#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
+#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
+#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
+#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
+#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
+
+#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
+# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
+#endif
+
+// Presence of C++14 language features:
+
+#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
+
+// Presence of C++17 language features:
+
+#define nssv_HAVE_NODISCARD nssv_CPP17_000
+
+// Presence of C++ library features:
+
+#define nssv_HAVE_STD_HASH nssv_CPP11_120
+
+// C++ feature usage:
+
+#if nssv_HAVE_CONSTEXPR_11
+# define nssv_constexpr constexpr
+#else
+# define nssv_constexpr /*constexpr*/
+#endif
+
+#if nssv_HAVE_CONSTEXPR_14
+# define nssv_constexpr14 constexpr
+#else
+# define nssv_constexpr14 /*constexpr*/
+#endif
+
+#if nssv_HAVE_EXPLICIT_CONVERSION
+# define nssv_explicit explicit
+#else
+# define nssv_explicit /*explicit*/
+#endif
+
+#if nssv_HAVE_INLINE_NAMESPACE
+# define nssv_inline_ns inline
+#else
+# define nssv_inline_ns /*inline*/
+#endif
+
+#if nssv_HAVE_NOEXCEPT
+# define nssv_noexcept noexcept
+#else
+# define nssv_noexcept /*noexcept*/
+#endif
+
+//#if nssv_HAVE_REF_QUALIFIER
+//# define nssv_ref_qual &
+//# define nssv_refref_qual &&
+//#else
+//# define nssv_ref_qual /*&*/
+//# define nssv_refref_qual /*&&*/
+//#endif
+
+#if nssv_HAVE_NULLPTR
+# define nssv_nullptr nullptr
+#else
+# define nssv_nullptr NULL
+#endif
+
+#if nssv_HAVE_NODISCARD
+# define nssv_nodiscard [[nodiscard]]
+#else
+# define nssv_nodiscard /*[[nodiscard]]*/
+#endif
+
+// Additional includes:
+
+#include
+#include
+#include
+#include
+#include
+#include // std::char_traits<>
+
+#if ! nssv_CONFIG_NO_EXCEPTIONS
+# include
+#endif
+
+#if nssv_CPP11_OR_GREATER
+# include
+#endif
+
+// Clang, GNUC, MSVC warning suppression macros:
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wuser-defined-literals"
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wliteral-suffix"
+#endif // __clang__
+
+#if nssv_COMPILER_MSVC_VERSION >= 140
+# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
+# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) )
+# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes))
+#else
+# define nssv_SUPPRESS_MSGSL_WARNING(expr)
+# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
+# define nssv_DISABLE_MSVC_WARNINGS(codes)
+#endif
+
+#if defined(__clang__)
+# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
+#elif defined(__GNUC__)
+# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
+#elif nssv_COMPILER_MSVC_VERSION >= 140
+# define nssv_RESTORE_WARNINGS() __pragma(warning(pop ))
+#else
+# define nssv_RESTORE_WARNINGS()
+#endif
+
+// Suppress the following MSVC (GSL) warnings:
+// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
+// start with an underscore are reserved
+// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
+// use brace initialization, gsl::narrow_cast or gsl::narow
+// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+
+nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
+//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
+//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
+
+namespace nonstd { namespace sv_lite {
+
+template
+<
+ class CharT,
+ class Traits = std::char_traits
+>
+class basic_string_view;
+
+//
+// basic_string_view:
+//
+
+template
+<
+ class CharT,
+ class Traits /* = std::char_traits */
+>
+class basic_string_view
+{
+public:
+ // Member types:
+
+ typedef Traits traits_type;
+ typedef CharT value_type;
+
+ typedef CharT * pointer;
+ typedef CharT const * const_pointer;
+ typedef CharT & reference;
+ typedef CharT const & const_reference;
+
+ typedef const_pointer iterator;
+ typedef const_pointer const_iterator;
+ typedef std::reverse_iterator< const_iterator > reverse_iterator;
+ typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
+
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ // 24.4.2.1 Construction and assignment:
+
+ nssv_constexpr basic_string_view() nssv_noexcept
+ : data_( nssv_nullptr )
+ , size_( 0 )
+ {}
+
+#if nssv_CPP11_OR_GREATER
+ nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
+#else
+ nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
+ : data_( other.data_)
+ , size_( other.size_)
+ {}
+#endif
+
+ nssv_constexpr basic_string_view( CharT const * s, size_type count )
+ : data_( s )
+ , size_( count )
+ {}
+
+ nssv_constexpr basic_string_view( CharT const * s)
+ : data_( s )
+ , size_( Traits::length(s) )
+ {}
+
+ // Assignment:
+
+#if nssv_CPP11_OR_GREATER
+ nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
+#else
+ nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
+ {
+ data_ = other.data_;
+ size_ = other.size_;
+ return *this;
+ }
+#endif
+
+ // 24.4.2.2 Iterator support:
+
+ nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
+ nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
+
+ nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
+ nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
+
+ nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); }
+ nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); }
+
+ nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
+ nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
+
+ // 24.4.2.3 Capacity:
+
+ nssv_constexpr size_type size() const nssv_noexcept { return size_; }
+ nssv_constexpr size_type length() const nssv_noexcept { return size_; }
+ nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
+
+ // since C++20
+ nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
+ {
+ return 0 == size_;
+ }
+
+ // 24.4.2.4 Element access:
+
+ nssv_constexpr const_reference operator[]( size_type pos ) const
+ {
+ return data_at( pos );
+ }
+
+ nssv_constexpr14 const_reference at( size_type pos ) const
+ {
+#if nssv_CONFIG_NO_EXCEPTIONS
+ assert( pos < size() );
+#else
+ if ( pos >= size() )
+ {
+ throw std::out_of_range("nonst::string_view::at()");
+ }
+#endif
+ return data_at( pos );
+ }
+
+ nssv_constexpr const_reference front() const { return data_at( 0 ); }
+ nssv_constexpr const_reference back() const { return data_at( size() - 1 ); }
+
+ nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
+
+ // 24.4.2.5 Modifiers:
+
+ nssv_constexpr14 void remove_prefix( size_type n )
+ {
+ assert( n <= size() );
+ data_ += n;
+ size_ -= n;
+ }
+
+ nssv_constexpr14 void remove_suffix( size_type n )
+ {
+ assert( n <= size() );
+ size_ -= n;
+ }
+
+ nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
+ {
+ using std::swap;
+ swap( data_, other.data_ );
+ swap( size_, other.size_ );
+ }
+
+ // 24.4.2.6 String operations:
+
+ size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
+ {
+#if nssv_CONFIG_NO_EXCEPTIONS
+ assert( pos <= size() );
+#else
+ if ( pos > size() )
+ {
+ throw std::out_of_range("nonst::string_view::copy()");
+ }
+#endif
+ const size_type rlen = (std::min)( n, size() - pos );
+
+ (void) Traits::copy( dest, data() + pos, rlen );
+
+ return rlen;
+ }
+
+ nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
+ {
+#if nssv_CONFIG_NO_EXCEPTIONS
+ assert( pos <= size() );
+#else
+ if ( pos > size() )
+ {
+ throw std::out_of_range("nonst::string_view::substr()");
+ }
+#endif
+ return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
+ }
+
+ // compare(), 6x:
+
+ nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
+ {
+ if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+ return result;
+
+ return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
+ {
+ return substr( pos1, n1 ).compare( other );
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
+ {
+ return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
+ }
+
+ nssv_constexpr int compare( CharT const * s ) const // (4)
+ {
+ return compare( basic_string_view( s ) );
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
+ {
+ return substr( pos1, n1 ).compare( basic_string_view( s ) );
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
+ {
+ return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
+ }
+
+ // 24.4.2.7 Searching:
+
+ // starts_with(), 3x, since C++20:
+
+ nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1)
+ {
+ return size() >= v.size() && compare( 0, v.size(), v ) == 0;
+ }
+
+ nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2)
+ {
+ return starts_with( basic_string_view( &c, 1 ) );
+ }
+
+ nssv_constexpr bool starts_with( CharT const * s ) const // (3)
+ {
+ return starts_with( basic_string_view( s ) );
+ }
+
+ // ends_with(), 3x, since C++20:
+
+ nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1)
+ {
+ return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
+ }
+
+ nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2)
+ {
+ return ends_with( basic_string_view( &c, 1 ) );
+ }
+
+ nssv_constexpr bool ends_with( CharT const * s ) const // (3)
+ {
+ return ends_with( basic_string_view( s ) );
+ }
+
+ // find(), 4x:
+
+ nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
+ {
+ return assert( v.size() == 0 || v.data() != nssv_nullptr )
+ , pos >= size()
+ ? npos
+ : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
+ }
+
+ nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
+ {
+ return find( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3)
+ {
+ return find( basic_string_view( s, n ), pos );
+ }
+
+ nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4)
+ {
+ return find( basic_string_view( s ), pos );
+ }
+
+ // rfind(), 4x:
+
+ nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
+ {
+ if ( size() < v.size() )
+ return npos;
+
+ if ( v.empty() )
+ return (std::min)( size(), pos );
+
+ const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
+ const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
+
+ return result != last ? size_type( result - cbegin() ) : npos;
+ }
+
+ nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
+ {
+ return rfind( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3)
+ {
+ return rfind( basic_string_view( s, n ), pos );
+ }
+
+ nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4)
+ {
+ return rfind( basic_string_view( s ), pos );
+ }
+
+ // find_first_of(), 4x:
+
+ nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
+ {
+ return pos >= size()
+ ? npos
+ : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
+ }
+
+ nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
+ {
+ return find_first_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3)
+ {
+ return find_first_of( basic_string_view( s, n ), pos );
+ }
+
+ nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4)
+ {
+ return find_first_of( basic_string_view( s ), pos );
+ }
+
+ // find_last_of(), 4x:
+
+ nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
+ {
+ return empty()
+ ? npos
+ : pos >= size()
+ ? find_last_of( v, size() - 1 )
+ : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
+ }
+
+ nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
+ {
+ return find_last_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3)
+ {
+ return find_last_of( basic_string_view( s, count ), pos );
+ }
+
+ nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4)
+ {
+ return find_last_of( basic_string_view( s ), pos );
+ }
+
+ // find_first_not_of(), 4x:
+
+ nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
+ {
+ return pos >= size()
+ ? npos
+ : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
+ }
+
+ nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
+ {
+ return find_first_not_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
+ {
+ return find_first_not_of( basic_string_view( s, count ), pos );
+ }
+
+ nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4)
+ {
+ return find_first_not_of( basic_string_view( s ), pos );
+ }
+
+ // find_last_not_of(), 4x:
+
+ nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
+ {
+ return empty()
+ ? npos
+ : pos >= size()
+ ? find_last_not_of( v, size() - 1 )
+ : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
+ }
+
+ nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
+ {
+ return find_last_not_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
+ {
+ return find_last_not_of( basic_string_view( s, count ), pos );
+ }
+
+ nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4)
+ {
+ return find_last_not_of( basic_string_view( s ), pos );
+ }
+
+ // Constants:
+
+#if nssv_CPP17_OR_GREATER
+ static nssv_constexpr size_type npos = size_type(-1);
+#elif nssv_CPP11_OR_GREATER
+ enum : size_type { npos = size_type(-1) };
+#else
+ enum { npos = size_type(-1) };
+#endif
+
+private:
+ struct not_in_view
+ {
+ const basic_string_view v;
+
+ nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {}
+
+ nssv_constexpr bool operator()( CharT c ) const
+ {
+ return npos == v.find_first_of( c );
+ }
+ };
+
+ nssv_constexpr size_type to_pos( const_iterator it ) const
+ {
+ return it == cend() ? npos : size_type( it - cbegin() );
+ }
+
+ nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
+ {
+ return it == crend() ? npos : size_type( crend() - it - 1 );
+ }
+
+ nssv_constexpr const_reference data_at( size_type pos ) const
+ {
+#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
+ return data_[pos];
+#else
+ return assert( pos < size() ), data_[pos];
+#endif
+ }
+
+private:
+ const_pointer data_;
+ size_type size_;
+
+public:
+#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+
+ template< class Allocator >
+ basic_string_view( std::basic_string const & s ) nssv_noexcept
+ : data_( s.data() )
+ , size_( s.size() )
+ {}
+
+#if nssv_HAVE_EXPLICIT_CONVERSION
+
+ template< class Allocator >
+ explicit operator std::basic_string() const
+ {
+ return to_string( Allocator() );
+ }
+
+#endif // nssv_HAVE_EXPLICIT_CONVERSION
+
+#if nssv_CPP11_OR_GREATER
+
+ template< class Allocator = std::allocator >
+ std::basic_string
+ to_string( Allocator const & a = Allocator() ) const
+ {
+ return std::basic_string( begin(), end(), a );
+ }
+
+#else
+
+ std::basic_string
+ to_string() const
+ {
+ return std::basic_string( begin(), end() );
+ }
+
+ template< class Allocator >
+ std::basic_string
+ to_string( Allocator const & a ) const
+ {
+ return std::basic_string( begin(), end(), a );
+ }
+
+#endif // nssv_CPP11_OR_GREATER
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+};
+
+//
+// Non-member functions:
+//
+
+// 24.4.3 Non-member comparison functions:
+// lexicographically compare two string views (function template):
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator== (
+ basic_string_view lhs,
+ basic_string_view rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) == 0 ; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator!= (
+ basic_string_view lhs,
+ basic_string_view rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) != 0 ; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator< (
+ basic_string_view lhs,
+ basic_string_view rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0 ; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator<= (
+ basic_string_view lhs,
+ basic_string_view rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0 ; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator> (
+ basic_string_view lhs,
+ basic_string_view rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0 ; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator>= (
+ basic_string_view lhs,
+ basic_string_view rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0 ; }
+
+// Let S be basic_string_view, and sv be an instance of S.
+// Implementations shall provide sufficient additional overloads marked
+// constexpr and noexcept so that an object t with an implicit conversion
+// to S can be compared according to Table 67.
+
+#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+
+#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type
+
+#if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 )
+# define nssv_MSVC_ORDER(x) , int=x
+#else
+# define nssv_MSVC_ORDER(x) /*, int=x*/
+#endif
+
+// ==
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator==(
+ basic_string_view