diff --git a/README.MD b/README.MD
index 764b0562..971cdf31 100644
--- a/README.MD
+++ b/README.MD
@@ -74,7 +74,7 @@ Install deb dependencies
# assume you have sourced ROS environment, same blow
sudo apt install libgflags-dev nlohmann-json3-dev \
ros-$ROS_DISTRO-image-transport ros-$ROS_DISTRO-image-publisher ros-$ROS_DISTRO-camera-info-manager \
-ros-$ROS_DISTRO-diagnostic-updater ros-$ROS_DISTRO-diagnostic-msgs
+ros-$ROS_DISTRO-diagnostic-updater ros-$ROS_DISTRO-diagnostic-msgs ros-$ROS_DISTRO-statistics-msgs
```
Install udev rules.
diff --git a/orbbec_camera/CMakeLists.txt b/orbbec_camera/CMakeLists.txt
index 17ca7334..eac2b66e 100644
--- a/orbbec_camera/CMakeLists.txt
+++ b/orbbec_camera/CMakeLists.txt
@@ -47,6 +47,7 @@ set(dependencies
Threads
diagnostic_updater
diagnostic_msgs
+ statistics_msgs
)
foreach (dep IN LISTS dependencies)
@@ -210,6 +211,8 @@ add_orbbec_executable(list_devices_node tools/list_devices_node.cpp)
add_orbbec_executable(list_depth_work_mode_node tools/list_depth_work_mode.cpp)
add_orbbec_executable(list_camera_profile_mode_node tools/list_camera_profile.cpp)
+add_orbbec_executable(topic_statistics_node tools/topic_statistics.cpp)
+
# Install rules
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION lib
@@ -232,6 +235,7 @@ endif ()
install(TARGETS list_devices_node
list_depth_work_mode_node
list_camera_profile_mode_node
+ topic_statistics_node
DESTINATION lib/${PROJECT_NAME}/)
if (BUILD_TESTING)
diff --git a/orbbec_camera/launch/gemini2L.launch.py b/orbbec_camera/launch/gemini2L.launch.py
index 38db185d..f5599ef2 100644
--- a/orbbec_camera/launch/gemini2L.launch.py
+++ b/orbbec_camera/launch/gemini2L.launch.py
@@ -25,7 +25,7 @@ def generate_launch_description():
DeclareLaunchArgument('connection_delay', default_value='100'),
DeclareLaunchArgument('color_width', default_value='1280'),
DeclareLaunchArgument('color_height', default_value='800'),
- DeclareLaunchArgument('color_fps', default_value='10'),
+ DeclareLaunchArgument('color_fps', default_value='30'),
DeclareLaunchArgument('color_format', default_value='MJPG'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('flip_color', default_value='false'),
@@ -38,7 +38,7 @@ def generate_launch_description():
DeclareLaunchArgument('color_white_balance', default_value='-1'),
DeclareLaunchArgument('depth_width', default_value='1280'),
DeclareLaunchArgument('depth_height', default_value='800'),
- DeclareLaunchArgument('depth_fps', default_value='15'),
+ DeclareLaunchArgument('depth_fps', default_value='30'),
DeclareLaunchArgument('depth_format', default_value='Y16'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('flip_depth', default_value='false'),
diff --git a/orbbec_camera/package.xml b/orbbec_camera/package.xml
index d7be7052..77cdd792 100644
--- a/orbbec_camera/package.xml
+++ b/orbbec_camera/package.xml
@@ -22,6 +22,7 @@
sensor_msgs
std_msgs
std_srvs
+ statistics_msgs
tf2
tf2_ros
tf2_sensor_msgs
diff --git a/orbbec_camera/scripts/plot_stat.py b/orbbec_camera/scripts/plot_stat.py
new file mode 100644
index 00000000..fb55d21f
--- /dev/null
+++ b/orbbec_camera/scripts/plot_stat.py
@@ -0,0 +1,55 @@
+import pandas as pd
+import matplotlib.pyplot as plt
+import matplotlib.dates as mdates
+import numpy as np
+
+def process_data(data):
+ # 处理数据,将含有 "ms" 的数据转换为浮点数
+ for col in ['min', 'avg', 'max']:
+ data[col] = data[col].str.replace(' ms', '').astype(float)
+ return data
+
+def split_data(data):
+ # 分别筛选出age和period的数据
+ age_data = data[data['message_type'] == 'age']
+ period_data = data[data['message_type'] == 'period']
+ return age_data, period_data
+
+def plot_data(data, title, y_ticks):
+ plt.figure(figsize=(12, 6))
+
+ for col, color in zip(['min', 'avg', 'max'], ['green', 'yellow', 'cyan']):
+ # Convert datetime to numpy array
+ x = data['_time'].to_numpy()
+ y = data[col].to_numpy()
+ plt.plot(x, y, label=col, color=color, marker='o', linestyle='-')
+
+ plt.ylabel('Milliseconds')
+ plt.title(title)
+
+ plt.gca().xaxis.set_major_locator(mdates.MinuteLocator(interval=10))
+ plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
+
+ plt.legend()
+ plt.grid(True, which='both')
+ plt.xticks(rotation=45)
+ plt.yticks(range(0, y_ticks[1], y_ticks[0]))
+ plt.tight_layout()
+ plt.show()
+
+def main():
+ plt.style.use('dark_background')
+
+ data = pd.read_csv('statistics.csv')
+
+ data['_time'] = pd.to_datetime(data['_time'])
+
+ data = process_data(data)
+ age_data, period_data = split_data(data)
+
+ plot_data(age_data, 'Message Age', (100, 501))
+
+ plot_data(period_data, 'Message Period', (20, 101))
+
+if __name__ == "__main__":
+ main()
diff --git a/orbbec_camera/tools/csv.hpp b/orbbec_camera/tools/csv.hpp
new file mode 100644
index 00000000..811c8e14
--- /dev/null
+++ b/orbbec_camera/tools/csv.hpp
@@ -0,0 +1,8585 @@
+#pragma once
+/*
+CSV for C++, version 2.3.0
+https://github.com/vincentlaucsb/csv-parser
+
+MIT License
+
+Copyright (c) 2017-2024 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