Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ad75d9f82 | |||
| eafe6f5a21 | |||
| 2fe6f0b4e1 | |||
| dff3303733 | |||
| a21c7312f5 | |||
| 11d64d12a0 | |||
| b6a8c83e0b | |||
| c4d83b0310 | |||
| 858f7533e6 | |||
| 43dc4b6654 | |||
| e636ba47fe | |||
| 13ce1550e2 | |||
| 0ac55f3699 | |||
| 1d06516181 | |||
| 26b7723f19 | |||
| c054600b05 | |||
| eb10cf1375 | |||
| 503efeb6b5 | |||
| 6372262304 | |||
| 7ee0beb343 | |||
| dfa4202e55 | |||
| 5eb7821cfc | |||
| d4ab8fb401 | |||
| 3c9b318218 | |||
| 4e764d2205 | |||
| 50f6f81a05 | |||
| 2f64eb40f0 | |||
| 1165165657 | |||
| 31327248e0 | |||
| d9cba881ac | |||
| 272b5c1238 | |||
| 2ecbff1cdd | |||
| fb19bdec67 | |||
| 7f47a4cbf8 | |||
| 5a6d7023ac | |||
| 329a79c3ea | |||
| 5ce29908df | |||
| 61987d937b | |||
| cdd51a6e3a | |||
| 2a69be29a6 | |||
| 37e02ca871 | |||
| 3d17d3ec8c | |||
| 1bc234cbca | |||
| 24ef556ab7 | |||
| 1799c1640b | |||
| 00355a64c6 | |||
| e5d8c0f567 | |||
| d5a0f1927d | |||
| 588835d80a | |||
| da5eb13d94 | |||
| 6937f1a885 | |||
| a2ba8b9eb6 | |||
| 56d680e036 | |||
| a7474d5939 | |||
| 6f07577bf0 | |||
| 35b64db13c | |||
| feca03e9c9 | |||
| 1acce4c588 | |||
| e96899ceba | |||
| 8ab328fcfe | |||
| fd22069562 | |||
| 058ebe026e | |||
| 99b0ac2ed3 | |||
| 2578895b40 | |||
| 9a55d8594b | |||
| 8b6b421c52 | |||
| 5e49ed5f9a | |||
| 075a0b0af2 | |||
| 3fc89111ea | |||
| e5cf445f19 | |||
| d9f44cfeda | |||
| 1ece6dccca | |||
| e576229d90 | |||
| 30bad78580 | |||
| 53378eb6c3 | |||
| 54c8e24fab | |||
| 7f9a1195fa | |||
| 11ea9db137 | |||
| adc52a3659 | |||
| a838f1a25a | |||
| f537cb8bdf | |||
| 2e7d214660 | |||
| bb6cc3e783 | |||
| 700a3484ac | |||
| 7cfe707bc3 | |||
| 8a3b5045cd | |||
| ffcd4962fe | |||
| 4cdcf6e165 | |||
| 4f379d75ec | |||
| 01e78a1a01 | |||
| a17886b706 | |||
| c5776e8d2f |
@ -1,45 +1,93 @@
|
||||
project(librexy)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
set(librexy_VERSION_STRING "000010000L")
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1)
|
||||
|
||||
set(librexy_VERSION_STRING "000020000L")
|
||||
set(librexy_VERSION_MAJOR 0)
|
||||
set(librexy_VERSION_MINOR 1)
|
||||
set(librexy_VERSION_MINOR 2)
|
||||
set(librexy_VERSION_REVISION 0)
|
||||
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
|
||||
include_directories(BEFORE SYSTEM "${INCLUDE_PATH}")
|
||||
|
||||
#find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck)
|
||||
#if(CMAKE_CXX_CPPCHECK)
|
||||
# list(APPEND CMAKE_CXX_CPPCHECK "--enable=warning" "--inconclusive" "--force" "--inline-suppr")
|
||||
#endif()
|
||||
|
||||
option(ENABLE_SHARED "Build shared library" ON)
|
||||
cmake_dependent_option(ENABLE_SHARED "Build shared library" ON "NOT BUILD_HEADER_ONLY" OFF)
|
||||
cmake_dependent_option(ENABLE_SSO "Use small string optimization" ON "NOT BUILD_HEADER_ONLY" ON)
|
||||
option(ENABLE_PROFILING "Enable asan" OFF)
|
||||
option(BUILD_TESTS "Enable testing" OFF)
|
||||
option(BUILD_HEADER_ONLY "Enable header only build" OFF)
|
||||
mark_as_advanced(ENABLE_PROFILING)
|
||||
|
||||
set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/binary.cpp" "src/static_string.cpp" "src/threadpool.cpp")
|
||||
add_library(ensure OBJECT "src/ensure.cpp")
|
||||
target_compile_options(ensure PRIVATE -Wall -Wextra -pedantic -std=c++17)
|
||||
if(ENABLE_SHARED)
|
||||
if(MSVC)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
|
||||
endif()
|
||||
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
set(LIBREXY_PUBLIC_HEADERS "include/rexy/rexy.hpp" "include/rexy/algorithm.hpp" "include/rexy/algorithm.tpp" "include/rexy/utility.hpp" "include/rexy/hash.hpp" "include/rexy/mpmc_queue.hpp" "include/rexy/mpmc_queue.tpp" "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/expression.hpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/string_base.tpp" "include/rexy/allocator.hpp" "include/rexy/meta.hpp" "include/rexy/buffer.hpp" "include/rexy/buffer.tpp" "include/rexy/debug_print.hpp" "include/rexy/deferred.hpp" "include/rexy/enum_traits.hpp" "include/rexy/storage_for.hpp" "include/rexy/storage_for.tpp" "include/rexy/visitor.hpp" "include/rexy/string_view.hpp" "include/rexy/string_view.tpp" "include/rexy/format.hpp" "include/rexy/format.tpp" "include/rexy/list.hpp" "include/rexy/list.tpp")
|
||||
|
||||
if(BUILD_HEADER_ONLY)
|
||||
set(LIBREXY_BUILT_LIBRARY_HEADERS "")
|
||||
set(librexy_HEADER_ONLY_BUILD 1)
|
||||
|
||||
add_library(rexy INTERFACE)
|
||||
set(LIBREXY_LIBFLAGS "")
|
||||
else()
|
||||
set(LIBREXY_BUILT_LIBRARY_HEADERS "include/rexy/filerd.hpp" "include/rexy/threadpool.hpp" "include/rexy/demangle.hpp")
|
||||
set(librexy_HEADER_ONLY_BUILD 0)
|
||||
set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/string_view.cpp" "src/threadpool.cpp" "src/demangle.cpp")
|
||||
|
||||
if(ENABLE_SHARED)
|
||||
add_library(rexy SHARED ${SOURCE_LIST})
|
||||
set_target_properties(rexy PROPERTIES SOVERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}.${librexy_VERSION_REVISION}")
|
||||
set_target_properties(rexy PROPERTIES SOVERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}")
|
||||
set(LIBREXY_LIBFLAGS "-lrexy")
|
||||
target_link_libraries(rexy "-lpthread")
|
||||
else()
|
||||
else()
|
||||
add_library(rexy STATIC ${SOURCE_LIST})
|
||||
set(LIBREXY_LIBFLAGS "-lrexy -lpthread")
|
||||
target_link_libraries(rexy "-lpthread")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_PROFILING)
|
||||
if(MSVC)
|
||||
#make msvc update the __cplusplus macro to actually comply to c++ standard
|
||||
target_compile_options(rexy PRIVATE "/Zc:__cplusplus")
|
||||
else()
|
||||
target_compile_options(rexy PRIVATE "-Wall" "-Wextra" "-pedantic")
|
||||
endif()
|
||||
set_target_properties(rexy PROPERTIES VERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}.${librexy_VERSION_REVISION}")
|
||||
|
||||
if(ENABLE_SSO)
|
||||
set(librexy_ENABLE_SSO 1)
|
||||
else()
|
||||
set(librexy_ENABLE_SSO 0)
|
||||
endif()
|
||||
if(ENABLE_PROFILING)
|
||||
target_compile_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_library(ensure OBJECT "src/ensure.cpp")
|
||||
if(MSVC)
|
||||
target_compile_options(ensure PRIVATE "/Zc:__cplusplus")
|
||||
else()
|
||||
target_compile_options(ensure PRIVATE "-Wall" "-Wextra" "-pedantic")
|
||||
endif()
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
set(LIBREXY_PUBLIC_HEADERS "include/rexy/rexy.hpp" "include/rexy/algorithm.hpp" "include/rexy/utility.hpp" "include/rexy/basic_string_hash.hpp" "include/rexy/hash.hpp" "include/rexy/static_string_hash.hpp" "include/rexy/string_hash.hpp" "include/rexy/mpmc_queue.hpp" "include/rexy/mpmc_queue.tpp" "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/binary.hpp" "include/rexy/expression.hpp" "include/rexy/binary_base.hpp" "include/rexy/binary_base.tpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/filerd.hpp" "include/rexy/string_base.tpp" "include/rexy/allocator.hpp" "include/rexy/meta.hpp")
|
||||
target_compile_options(rexy PRIVATE -Wall -Wextra -pedantic -std=c++17)
|
||||
|
||||
install(TARGETS rexy
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
@ -49,8 +97,11 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
)
|
||||
install(FILES ${LIBREXY_PUBLIC_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy/")
|
||||
install(FILES ${LIBREXY_BUILT_LIBRARY_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy/")
|
||||
install(DIRECTORY "include/rexy/detail" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
|
||||
install(DIRECTORY "include/rexy/cx" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
|
||||
install(DIRECTORY "include/rexy/compat" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
|
||||
install(DIRECTORY "include/rexy/concepts" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc.cmake.in"
|
||||
|
||||
@ -19,11 +19,7 @@
|
||||
#ifndef REXY_ALGORITHM_HPP
|
||||
#define REXY_ALGORITHM_HPP
|
||||
|
||||
#include "utility.hpp" //swap
|
||||
#include "rexy.hpp"
|
||||
#include <cstdint> //SIZE_MAX
|
||||
#include <cstdlib> //size_t
|
||||
#include <type_traits>
|
||||
|
||||
#include "detail/algorithm.hpp"
|
||||
|
||||
@ -33,84 +29,20 @@ namespace rexy{
|
||||
//right is one past the end of the list
|
||||
template<class Iter, class Compare>
|
||||
constexpr void quicksort(Iter left, Iter right, const Compare& cmp)
|
||||
noexcept(noexcept(detail::qs_partition(left, right, cmp)))
|
||||
{
|
||||
while(left < right){
|
||||
auto real_right = right-1;
|
||||
auto pivot = detail::qs_partition(left, real_right, cmp);
|
||||
quicksort(left, pivot, cmp);
|
||||
left = ++pivot;
|
||||
}
|
||||
}
|
||||
noexcept(noexcept(detail::qs_partition(left, right, cmp)));
|
||||
|
||||
//Requires Iterators to be LegacyRandomAccessIterators
|
||||
template<class HIter, class NIter>
|
||||
constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){
|
||||
size_t j = 0;
|
||||
size_t i = 0;
|
||||
size_t nlen = nend - nstart;
|
||||
size_t hlen = hend - hstart;
|
||||
auto [suffix, period] = detail::critical_factorization(nstart, nend);
|
||||
|
||||
if(detail::iter_compare(nstart, nstart + period, suffix)){
|
||||
size_t memory = SIZE_MAX;
|
||||
while(j <= hlen - nlen){
|
||||
i = max(suffix, memory) + 1;
|
||||
//right side
|
||||
while(i < nlen && nstart[i] == hstart[i + j]){
|
||||
++i;
|
||||
}
|
||||
if(i >= nlen){
|
||||
i = suffix;
|
||||
//left side
|
||||
while(i > memory && nstart[i] == hstart[i + j]){
|
||||
--i;
|
||||
}
|
||||
if(i <= memory){
|
||||
return hstart + j;
|
||||
}
|
||||
j += period;
|
||||
memory = nlen - period - 1;
|
||||
}else{
|
||||
j += (i - suffix);
|
||||
memory = SIZE_MAX;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
period = max(suffix + 1, nlen - suffix - 1) + 1;
|
||||
j = 0;
|
||||
while(j <= hlen - nlen){
|
||||
i = suffix + 1;
|
||||
//right side
|
||||
while(i < nlen && nstart[i] == hstart[i + j]){
|
||||
++i;
|
||||
}
|
||||
if(i >= nlen){
|
||||
i = suffix;
|
||||
//left side
|
||||
while(i != SIZE_MAX && nstart[i] == hstart[i + j]){
|
||||
--i;
|
||||
}
|
||||
if(i == SIZE_MAX){
|
||||
return hstart + j;
|
||||
}
|
||||
j += period;
|
||||
}else{
|
||||
j += (i - suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hend;
|
||||
}
|
||||
constexpr HIter two_way_search(HIter hstart, HIter hend, NIter nstart, NIter nend);
|
||||
|
||||
//searcher for use with generic search wrappers
|
||||
struct two_way_searcher{
|
||||
template<class HIter, class NIter>
|
||||
constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{
|
||||
return two_way_search(hstart, hend, nstart, nend);
|
||||
}
|
||||
constexpr HIter operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "algorithm.tpp"
|
||||
|
||||
#endif
|
||||
|
||||
108
include/rexy/algorithm.tpp
Normal file
108
include/rexy/algorithm.tpp
Normal file
@ -0,0 +1,108 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_ALGORITHM_TPP
|
||||
#define REXY_ALGORITHM_TPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include <limits> //numeric_limits
|
||||
|
||||
namespace rexy{
|
||||
|
||||
//Requires Iterators to be LegacyRandomAccessIterators
|
||||
//right is one past the end of the list
|
||||
template<class Iter, class Compare>
|
||||
constexpr void quicksort(Iter left, Iter right, const Compare& cmp)
|
||||
noexcept(noexcept(detail::qs_partition(left, right, cmp)))
|
||||
{
|
||||
while(left < right){
|
||||
auto real_right = right-1;
|
||||
auto pivot = detail::qs_partition(left, real_right, cmp);
|
||||
quicksort(left, pivot, cmp);
|
||||
left = ++pivot;
|
||||
}
|
||||
}
|
||||
|
||||
//Requires Iterators to be LegacyRandomAccessIterators
|
||||
template<class HIter, class NIter>
|
||||
constexpr HIter two_way_search(HIter hstart, HIter hend, NIter nstart, NIter nend){
|
||||
std::size_t j = 0;
|
||||
std::size_t i = 0;
|
||||
std::size_t nlen = nend - nstart;
|
||||
std::size_t hlen = hend - hstart;
|
||||
auto [suffix, period] = detail::critical_factorization(nstart, nend);
|
||||
|
||||
if(detail::iter_compare(nstart, nstart + period, suffix)){
|
||||
std::size_t memory = std::numeric_limits<std::size_t>::max();
|
||||
while(j <= hlen - nlen){
|
||||
i = max(suffix, memory) + 1;
|
||||
//right side
|
||||
while(i < nlen && nstart[i] == hstart[i + j]){
|
||||
++i;
|
||||
}
|
||||
if(i >= nlen){
|
||||
i = suffix;
|
||||
//left side
|
||||
while(i > memory && nstart[i] == hstart[i + j]){
|
||||
--i;
|
||||
}
|
||||
if(i <= memory){
|
||||
return hstart + j;
|
||||
}
|
||||
j += period;
|
||||
memory = nlen - period - 1;
|
||||
}else{
|
||||
j += (i - suffix);
|
||||
memory = std::numeric_limits<std::size_t>::max();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
period = max(suffix + 1, nlen - suffix - 1) + 1;
|
||||
j = 0;
|
||||
while(j <= hlen - nlen){
|
||||
i = suffix + 1;
|
||||
//right side
|
||||
while(i < nlen && nstart[i] == hstart[i + j]){
|
||||
++i;
|
||||
}
|
||||
if(i >= nlen){
|
||||
i = suffix;
|
||||
//left side
|
||||
while(i != std::numeric_limits<std::size_t>::max() && nstart[i] == hstart[i + j]){
|
||||
--i;
|
||||
}
|
||||
if(i == std::numeric_limits<std::size_t>::max()){
|
||||
return hstart + j;
|
||||
}
|
||||
j += period;
|
||||
}else{
|
||||
j += (i - suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hend;
|
||||
}
|
||||
|
||||
template<class HIter, class NIter>
|
||||
constexpr HIter two_way_searcher::operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const{
|
||||
return two_way_search(hstart, hend, nstart, nend);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -19,77 +19,12 @@
|
||||
#ifndef REXY_DEFAULT_ALLOCATOR_HPP
|
||||
#define REXY_DEFAULT_ALLOCATOR_HPP
|
||||
|
||||
#include <cstdlib> //size_t
|
||||
#include <type_traits> //true_type, false_type
|
||||
#include <new>
|
||||
#include <limits> //numeric_limits
|
||||
#include "compat/allocator.hpp"
|
||||
|
||||
#include "rexy.hpp"
|
||||
#include <type_traits> //declval
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T>
|
||||
struct allocator
|
||||
{
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using void_pointer = void*;
|
||||
using const_void_pointer = const void*;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using is_always_equal = std::true_type;
|
||||
using propagate_on_container_copy_assignment = std::false_type;
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using propagate_on_container_swap = std::false_type;
|
||||
|
||||
template<class U>
|
||||
struct rebind{
|
||||
using other = allocator<U>;
|
||||
};
|
||||
|
||||
private:
|
||||
constexpr bool has_overflow(size_type n)const{
|
||||
return n > max_size();
|
||||
}
|
||||
|
||||
public:
|
||||
allocator(void) = default;
|
||||
allocator(const allocator&) = default;
|
||||
allocator(allocator&&) = default;
|
||||
template<class U>
|
||||
constexpr allocator(const allocator<U>&)noexcept{}
|
||||
~allocator(void) = default;
|
||||
|
||||
pointer allocate(size_type n){
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
return reinterpret_cast<pointer>(::operator new(bytes));
|
||||
}else{
|
||||
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
|
||||
}
|
||||
}
|
||||
void deallocate(pointer p, size_type n){
|
||||
#if defined(__clang__) && !defined(__cpp_sized_deallocation)
|
||||
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p);
|
||||
}else{
|
||||
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#else
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p, bytes);
|
||||
}else{
|
||||
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
constexpr size_type max_size(void)const{
|
||||
return std::numeric_limits<size_type>::max()/sizeof(T);
|
||||
}
|
||||
};
|
||||
template<class T, class U>
|
||||
constexpr bool operator==(const allocator<T>&, const allocator<U>&){
|
||||
return true;
|
||||
@ -98,6 +33,14 @@ namespace rexy{
|
||||
constexpr bool operator!=(const allocator<T>&, const allocator<U>&){
|
||||
return false;
|
||||
}
|
||||
|
||||
template<REXY_ALLOCATOR_CONCEPT T>
|
||||
struct is_nothrow_allocator{
|
||||
static constexpr bool value = noexcept(std::declval<T>().allocate(0)) && noexcept(std::declval<T>().deallocate(nullptr, 0));
|
||||
};
|
||||
template<REXY_ALLOCATOR_CONCEPT T>
|
||||
static constexpr bool is_nothrow_allocator_v = is_nothrow_allocator<T>::value;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,257 +0,0 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BINARY_BASE_HPP
|
||||
#define REXY_BINARY_BASE_HPP
|
||||
|
||||
#include <cstdlib> //size_t
|
||||
#include <utility> //move
|
||||
#include <cstring> //memcpy
|
||||
#include <cstddef> //ptrdiff_t
|
||||
#include <type_traits>
|
||||
#include <iterator> //reverse_iterator
|
||||
|
||||
#include "utility.hpp" //max
|
||||
#include "steal.hpp"
|
||||
#include "expression.hpp"
|
||||
#include "traits.hpp"
|
||||
#include "detail/string_appender.hpp"
|
||||
#include "detail/hasallocator.hpp"
|
||||
#include "rexy.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
class binary_base
|
||||
{
|
||||
public:
|
||||
using value_type = char;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
protected:
|
||||
pointer m_data = nullptr;
|
||||
size_type m_size = 0;
|
||||
size_type m_cap = 0;
|
||||
protected:
|
||||
constexpr binary_base(void)noexcept = default;
|
||||
constexpr binary_base(size_type len)noexcept;
|
||||
constexpr binary_base(pointer data, size_type size)noexcept;
|
||||
constexpr binary_base(pointer data, size_type size, size_type cap)noexcept;
|
||||
constexpr binary_base(const binary_base& b)noexcept;
|
||||
~binary_base(void)noexcept = default;
|
||||
|
||||
public:
|
||||
constexpr pointer release(void)noexcept;
|
||||
|
||||
constexpr size_type size(void)const;
|
||||
constexpr size_type capacity(void)const;
|
||||
constexpr pointer get(void);
|
||||
constexpr const_pointer get(void)const;
|
||||
constexpr operator bool(void)const;
|
||||
|
||||
constexpr reference operator[](size_type i)noexcept;
|
||||
constexpr const_reference operator[](size_type i)const noexcept;
|
||||
|
||||
constexpr iterator begin(void);
|
||||
constexpr const_iterator begin(void)const;
|
||||
constexpr iterator end(void);
|
||||
constexpr const_iterator end(void)const;
|
||||
constexpr const_iterator cbegin(void)const;
|
||||
constexpr const_iterator cend(void)const;
|
||||
|
||||
constexpr reverse_iterator rbegin(void);
|
||||
constexpr const_reverse_iterator rbegin(void)const;
|
||||
constexpr reverse_iterator rend(void);
|
||||
constexpr const_reverse_iterator rend(void)const;
|
||||
constexpr const_reverse_iterator crbegin(void)const;
|
||||
constexpr const_reverse_iterator crend(void)const;
|
||||
};
|
||||
template<class Allocator>
|
||||
class basic_binary : protected detail::hasallocator<Allocator>, public binary_base
|
||||
{
|
||||
public:
|
||||
using allocator_type = Allocator;
|
||||
|
||||
public:
|
||||
constexpr basic_binary(void)noexcept;
|
||||
constexpr basic_binary(rexy::steal<pointer> data, size_type size)noexcept;
|
||||
constexpr basic_binary(rexy::steal<pointer> data, size_type cap, size_type size)noexcept;
|
||||
constexpr basic_binary(rexy::steal<pointer> data)noexcept;
|
||||
basic_binary(const_pointer data, size_type size)noexcept(noexcept(this->allocate(0)));
|
||||
basic_binary(const_pointer data)noexcept(noexcept(this->allocate(0)));
|
||||
basic_binary(const_pointer data, size_type size, size_type cap)noexcept(noexcept(this->allocate(0)));
|
||||
explicit basic_binary(size_type size)noexcept(noexcept(this->allocate(0)));
|
||||
basic_binary(size_type size, size_type cap)noexcept(noexcept(this->allocate(0)));
|
||||
|
||||
basic_binary(const basic_binary& b)noexcept(noexcept(this->allocate(0)));
|
||||
constexpr basic_binary(basic_binary&& b)noexcept;
|
||||
basic_binary(const binary_base& b)noexcept(noexcept(this->allocate(0)));
|
||||
|
||||
~basic_binary(void)noexcept(noexcept(this->deallocate(nullptr,0)));
|
||||
|
||||
basic_binary& operator=(const basic_binary& b)noexcept(noexcept(this->allocate(0)));
|
||||
constexpr basic_binary& operator=(basic_binary&& b)noexcept;
|
||||
basic_binary& operator=(const_pointer c)noexcept(noexcept(this->allocate(0)));
|
||||
basic_binary& operator=(const binary_base& b)noexcept(noexcept(this->allocate(0)));
|
||||
|
||||
void reset(void)
|
||||
noexcept(noexcept(this->deallocate(nullptr,0)));
|
||||
void reset(pointer val, size_type cap, size_type size = 0)
|
||||
noexcept(noexcept(this->deallocate(nullptr,0)));
|
||||
bool resize(size_type newsize)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
void append(const_pointer data, size_type len)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
|
||||
using detail::hasallocator<Allocator>::allocator;
|
||||
|
||||
private:
|
||||
basic_binary& _copy_data(const_pointer data, size_type len)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
};
|
||||
|
||||
template<class Left, class Right>
|
||||
class binary_cat_expr : public rexy::binary_expression<Left,Right>
|
||||
{
|
||||
private:
|
||||
using left_t = std::decay_t<Left>;
|
||||
using right_t = std::decay_t<Right>;
|
||||
static_assert(std::is_same<typename left_t::value_type,typename right_t::value_type>::value);
|
||||
public:
|
||||
using value_type = typename left_t::value_type;
|
||||
using size_type = decltype(typename left_t::size_type{0} + typename right_t::size_type{0});
|
||||
using difference_type = decltype(typename left_t::difference_type{0} - typename right_t::difference_type{0});
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
public:
|
||||
using binary_expression<Left,Right>::binary_expression;
|
||||
|
||||
constexpr binary_cat_expr(const binary_cat_expr&) = default;
|
||||
constexpr binary_cat_expr(binary_cat_expr&&) = default;
|
||||
|
||||
constexpr size_type size(void)const noexcept;
|
||||
|
||||
template<class Alloc>
|
||||
operator basic_binary<Alloc>(void)
|
||||
noexcept(std::is_nothrow_constructible<basic_binary<Alloc>, typename basic_binary<Alloc>::size_type>::value &&
|
||||
std::is_nothrow_invocable<detail::string_appender<basic_binary<Alloc>>,decltype(*this)>::value);
|
||||
};
|
||||
|
||||
class static_binary : public binary_base
|
||||
{
|
||||
public:
|
||||
constexpr static_binary(void)noexcept = default;
|
||||
constexpr static_binary(const_pointer str, size_type len)noexcept;
|
||||
constexpr static_binary(const_pointer str)noexcept;
|
||||
constexpr static_binary(const static_binary&)noexcept;
|
||||
constexpr static_binary(static_binary&&)noexcept;
|
||||
~static_binary(void)noexcept = default;
|
||||
|
||||
constexpr static_binary& operator=(const_pointer str)noexcept;
|
||||
constexpr static_binary& operator=(const static_binary& str)noexcept;
|
||||
constexpr static_binary& operator=(static_binary&& str)noexcept;
|
||||
};
|
||||
|
||||
template<class Left, class Right>
|
||||
binary_cat_expr(Left&&, Right&&) -> binary_cat_expr<Left&&,Right&&>;
|
||||
|
||||
template<class T>
|
||||
struct is_binary{
|
||||
static constexpr bool value = rexy::is_type<T,binary_base>::value || rexy::is_template_type<T,binary_cat_expr>::value;
|
||||
};
|
||||
template<class T>
|
||||
struct is_concrete_binary{
|
||||
static constexpr bool value = rexy::is_type<T,binary_base>::value;
|
||||
};
|
||||
namespace detail{
|
||||
template<class... Ts>
|
||||
using enable_if_binary = std::enable_if_t<(is_binary<Ts>::value && ...),int>;
|
||||
template<class... Ts>
|
||||
using enable_if_concrete_binary = std::enable_if_t<(is_concrete_binary<Ts>::value && ...),int>;
|
||||
|
||||
}
|
||||
|
||||
template<class Left, class Right, detail::enable_if_concrete_binary<Left,Right> = 0>
|
||||
bool operator==(Left&& l, Right&& r)noexcept{
|
||||
return l && r && l.size() == r.size() && l.capacity() == r.capacity() && !memcmp(l.get(), r.get(), l.size());
|
||||
}
|
||||
template<class Left, class Right, detail::enable_if_concrete_binary<Left,Right> = 0>
|
||||
bool operator!=(Left&& l, Right&& r)noexcept{
|
||||
return !(std::forward<Left>(l) == std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, class Right, detail::enable_if_binary<Left,Right> = 0>
|
||||
auto operator+(Left&& l, Right&& r)
|
||||
noexcept(noexcept(::new (nullptr) binary_cat_expr(std::forward<Left>(l), std::forward<Right>(r))))
|
||||
{
|
||||
return binary_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, detail::enable_if_binary<Left> = 0>
|
||||
auto operator+(Left&& l, const char* c)
|
||||
noexcept(noexcept(::new (nullptr) binary_cat_expr(std::forward<Left>(l), rexy::static_binary(c))))
|
||||
{
|
||||
return binary_cat_expr(std::forward<Left>(l), rexy::static_binary(c));
|
||||
}
|
||||
template<class Right, detail::enable_if_binary<Right> = 0>
|
||||
auto operator+(const char* c, Right&& r)
|
||||
noexcept(noexcept(::new (nullptr) binary_cat_expr(rexy::static_binary(c), std::forward<Right>(r))))
|
||||
{
|
||||
return binary_cat_expr(rexy::static_binary(c), std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, class Right, detail::enable_if_concrete_binary<Left> = 0, detail::enable_if_binary<Right> = 0>
|
||||
decltype(auto) operator+=(Left& l, Right&& r)
|
||||
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left,decltype(l + std::forward<Right>(r))>::value)
|
||||
{
|
||||
return l = (l + std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, detail::enable_if_concrete_binary<Left> = 0>
|
||||
decltype(auto) operator+=(Left& l, const char* c)
|
||||
noexcept(noexcept(l + c) && std::is_nothrow_assignable<Left, decltype(l + c)>::value)
|
||||
{
|
||||
return l = (l + c);
|
||||
}
|
||||
} //namespace rexy
|
||||
|
||||
#include "binary_base.tpp"
|
||||
|
||||
namespace{
|
||||
constexpr inline rexy::static_binary operator"" _sb(const char* str, size_t len)noexcept{
|
||||
return rexy::static_binary(str, len);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REXY_STRING_HPP
|
||||
#include "detail/binary_string_conv.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,313 +0,0 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BINARY_BASE_TPP
|
||||
#define REXY_BINARY_BASE_TPP
|
||||
|
||||
#include <cstdlib> //size_t
|
||||
#include <utility> //move
|
||||
#include <cstring> //memcpy
|
||||
#include <type_traits>
|
||||
#include "utility.hpp" //max
|
||||
#include "steal.hpp"
|
||||
#include "detail/string_appender.hpp"
|
||||
|
||||
#define STOP_STRICT_ALIAS_WARNING(x) (x)
|
||||
|
||||
namespace rexy{
|
||||
|
||||
constexpr binary_base::binary_base(size_type len)noexcept:
|
||||
m_cap(len){}
|
||||
constexpr binary_base::binary_base(pointer data, size_type size)noexcept:
|
||||
m_data(data), m_cap(size){}
|
||||
constexpr binary_base::binary_base(pointer data, size_type size, size_type cap)noexcept:
|
||||
m_data(data), m_size(size), m_cap(cap){}
|
||||
constexpr binary_base::binary_base(const binary_base&)noexcept{}
|
||||
|
||||
constexpr auto binary_base::release(void)noexcept -> pointer{
|
||||
return exchange(m_data, nullptr);
|
||||
}
|
||||
|
||||
constexpr auto binary_base::size(void)const -> size_type{
|
||||
return m_size;
|
||||
}
|
||||
constexpr auto binary_base::capacity(void)const -> size_type{
|
||||
return m_cap;
|
||||
}
|
||||
constexpr auto binary_base::get(void) -> pointer{
|
||||
return m_data;
|
||||
}
|
||||
constexpr auto binary_base::get(void)const -> const_pointer{
|
||||
return m_data;
|
||||
}
|
||||
constexpr binary_base::operator bool(void)const{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
constexpr auto binary_base::operator[](size_type i)noexcept -> reference{
|
||||
return m_data[i];
|
||||
}
|
||||
constexpr auto binary_base::operator[](size_type i)const noexcept -> const_reference{
|
||||
return m_data[i];
|
||||
}
|
||||
|
||||
constexpr auto binary_base::begin(void) -> iterator{
|
||||
return m_data;
|
||||
}
|
||||
constexpr auto binary_base::begin(void)const -> const_iterator{
|
||||
return m_data;
|
||||
}
|
||||
constexpr auto binary_base::end(void) -> iterator{
|
||||
return m_data + m_size;
|
||||
}
|
||||
constexpr auto binary_base::end(void)const -> const_iterator{
|
||||
return m_data + m_size;
|
||||
}
|
||||
constexpr auto binary_base::cbegin(void)const -> const_iterator{
|
||||
return m_data;
|
||||
}
|
||||
constexpr auto binary_base::cend(void)const -> const_iterator{
|
||||
return m_data + m_size;
|
||||
}
|
||||
constexpr auto binary_base::rbegin(void) -> reverse_iterator{
|
||||
return reverse_iterator(m_data + m_size);
|
||||
}
|
||||
constexpr auto binary_base::rbegin(void)const -> const_reverse_iterator{
|
||||
return reverse_iterator(m_data + m_size);
|
||||
}
|
||||
constexpr auto binary_base::rend(void) -> reverse_iterator{
|
||||
return reverse_iterator(m_data - 1);
|
||||
}
|
||||
constexpr auto binary_base::rend(void)const -> const_reverse_iterator{
|
||||
return reverse_iterator(m_data - 1);
|
||||
}
|
||||
constexpr auto binary_base::crbegin(void)const -> const_reverse_iterator{
|
||||
return rbegin();
|
||||
}
|
||||
constexpr auto binary_base::crend(void)const -> const_reverse_iterator{
|
||||
return rend();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
constexpr basic_binary<Allocator>::basic_binary(void)noexcept{}
|
||||
template<class Allocator>
|
||||
constexpr basic_binary<Allocator>::basic_binary(rexy::steal<pointer> data)noexcept:
|
||||
binary_base(data.value() ? strlen(data.value()) : 0)
|
||||
{
|
||||
m_data = data.value();
|
||||
m_size = m_cap;
|
||||
}
|
||||
template<class Allocator>
|
||||
constexpr basic_binary<Allocator>::basic_binary(rexy::steal<pointer> data, size_type size)noexcept:
|
||||
binary_base(data.value(), size){}
|
||||
template<class Allocator>
|
||||
constexpr basic_binary<Allocator>::basic_binary(rexy::steal<pointer> data, size_type cap, size_type size)noexcept:
|
||||
binary_base(data.value(), cap, size){}
|
||||
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(const_pointer data, size_type size)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
binary_base(size ? this->allocate(size) : nullptr, size)
|
||||
{
|
||||
if(size)
|
||||
memcpy(m_data, data, size);
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(const_pointer data)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
basic_binary(data ? this->allocate(strlen(data)) : nullptr, strlen(data))
|
||||
{
|
||||
if(data)
|
||||
memcpy(m_data, data, m_cap);
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(const_pointer data, size_type size, size_type cap)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
binary_base(size ? this->allocate(size) : nullptr, size, cap)
|
||||
{
|
||||
if(size)
|
||||
memcpy(m_data, data, size);
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(size_type size)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
binary_base(this->allocate(size), size){}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(size_type size, size_type cap)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
binary_base(size ? this->allocate(size) : nullptr, size, cap){}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(const basic_binary& b)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
binary_base(b.m_size ? this->allocate(b.m_size) : nullptr, b.m_size, b.m_size)
|
||||
{
|
||||
if(b.m_size)
|
||||
memcpy(m_data, b.m_data, b.m_size);
|
||||
}
|
||||
template<class Allocator>
|
||||
constexpr basic_binary<Allocator>::basic_binary(basic_binary&& b)noexcept:
|
||||
binary_base(exchange(b.m_data, nullptr), b.m_size, b.m_cap){}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::basic_binary(const binary_base& b)
|
||||
noexcept(noexcept(this->allocate(0))):
|
||||
binary_base(b.size() ? this->allocate(b.size()) : nullptr, b.size(), b.size())
|
||||
{
|
||||
if(b.size())
|
||||
memcpy(m_data, b.get(), b.size());
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>::~basic_binary(void)
|
||||
noexcept(noexcept(this->deallocate(nullptr,0)))
|
||||
{
|
||||
this->deallocate(m_data, m_cap);
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>& basic_binary<Allocator>::operator=(const basic_binary& b)
|
||||
noexcept(noexcept(this->allocate(0)))
|
||||
{
|
||||
return _copy_data(b.get(), b.size());
|
||||
}
|
||||
template<class Allocator>
|
||||
constexpr basic_binary<Allocator>& basic_binary<Allocator>::operator=(basic_binary&& b)noexcept{
|
||||
m_size = b.m_size;
|
||||
m_cap = b.m_cap;
|
||||
swap(m_data, b.m_data);
|
||||
return *this;
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>& basic_binary<Allocator>::operator=(const_pointer c)
|
||||
noexcept(noexcept(this->allocate(0)))
|
||||
{
|
||||
return _copy_data(c, strlen(c));
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>& basic_binary<Allocator>::operator=(const binary_base& b)
|
||||
noexcept(noexcept(this->allocate(0)))
|
||||
{
|
||||
return _copy_data(b.get(), b.size());
|
||||
}
|
||||
template<class Allocator>
|
||||
void basic_binary<Allocator>::reset(void)
|
||||
noexcept(noexcept(this->deallocate(nullptr,0)))
|
||||
{
|
||||
this->deallocate(m_data, m_cap);
|
||||
m_data = nullptr;
|
||||
m_cap = m_size = 0;
|
||||
}
|
||||
template<class Allocator>
|
||||
void basic_binary<Allocator>::reset(pointer val, size_type cap, size_type size)
|
||||
noexcept(noexcept(this->deallocate(nullptr,0)))
|
||||
{
|
||||
this->deallocate(m_data, m_cap);
|
||||
m_data = val;
|
||||
m_cap = cap;
|
||||
m_size = size;
|
||||
}
|
||||
template<class Allocator>
|
||||
bool basic_binary<Allocator>::resize(size_type newsize)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)))
|
||||
{
|
||||
if(newsize < m_cap)
|
||||
return false;
|
||||
basic_binary<allocator_type> tmp(newsize);
|
||||
if(!tmp)
|
||||
return false;
|
||||
memcpy(STOP_STRICT_ALIAS_WARNING(tmp).m_data, m_data, m_size);
|
||||
swap(m_data, STOP_STRICT_ALIAS_WARNING(tmp).m_data);
|
||||
m_cap = STOP_STRICT_ALIAS_WARNING(tmp).m_cap;
|
||||
return true;
|
||||
}
|
||||
template<class Allocator>
|
||||
void basic_binary<Allocator>::append(const_pointer data, size_type len)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)))
|
||||
{
|
||||
if(m_size + len > m_cap)
|
||||
resize(max(m_cap*2, m_size+len));
|
||||
memcpy(m_data+m_size, data, len);
|
||||
m_size += len;
|
||||
}
|
||||
template<class Allocator>
|
||||
basic_binary<Allocator>& basic_binary<Allocator>::_copy_data(const_pointer data, size_type len)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)))
|
||||
{
|
||||
if(!len)
|
||||
return (*this = basic_binary(rexy::steal<pointer>(nullptr), 0, 0));
|
||||
if(len <= m_size){
|
||||
m_size = len;
|
||||
memcpy(m_data, data, len);
|
||||
return *this;
|
||||
}
|
||||
return (*this = basic_binary(data, len));
|
||||
}
|
||||
|
||||
constexpr static_binary::static_binary(const_pointer str, size_type len)noexcept:
|
||||
binary_base(const_cast<pointer>(str), len, len){}
|
||||
constexpr static_binary::static_binary(const_pointer str)noexcept:
|
||||
static_binary(str, strlen(str)){}
|
||||
constexpr static_binary::static_binary(const static_binary& s)noexcept:
|
||||
static_binary(s.get(), s.size()){}
|
||||
constexpr static_binary::static_binary(static_binary&& s)noexcept:
|
||||
static_binary(s.get(), s.size()){}
|
||||
|
||||
constexpr static_binary& static_binary::operator=(const_pointer str)noexcept{
|
||||
m_data = const_cast<pointer>(str);
|
||||
m_size = strlen(str);
|
||||
m_cap = m_size;
|
||||
return *this;
|
||||
}
|
||||
constexpr static_binary& static_binary::operator=(const static_binary& str)noexcept{
|
||||
m_data = str.m_data;
|
||||
m_size = str.m_size;
|
||||
m_cap = str.m_cap;
|
||||
return *this;
|
||||
}
|
||||
constexpr static_binary& static_binary::operator=(static_binary&& str)noexcept{
|
||||
m_data = str.m_data;
|
||||
m_size = str.m_size;
|
||||
m_cap = str.m_cap;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Left, class Right>
|
||||
constexpr auto binary_cat_expr<Left,Right>::size(void)const noexcept -> size_type{
|
||||
return this->m_l.size() + this->m_r.size();
|
||||
}
|
||||
|
||||
template<class Left, class Right>
|
||||
template<class Alloc>
|
||||
binary_cat_expr<Left,Right>::operator basic_binary<Alloc>(void)
|
||||
noexcept(std::is_nothrow_constructible<basic_binary<Alloc>, typename basic_binary<Alloc>::size_type>::value &&
|
||||
std::is_nothrow_invocable<detail::string_appender<basic_binary<Alloc>>,decltype(*this)>::value)
|
||||
{
|
||||
auto sz = size();
|
||||
basic_binary<Alloc> ret(sz);
|
||||
|
||||
detail::string_appender<basic_binary<Alloc>> append(ret);
|
||||
|
||||
append(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef STOP_STRICT_ALIAS_WARNING
|
||||
|
||||
#endif
|
||||
106
include/rexy/buffer.hpp
Normal file
106
include/rexy/buffer.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2021 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BUFFER_HPP
|
||||
#define REXY_BUFFER_HPP
|
||||
|
||||
#include "allocator.hpp"
|
||||
#include "detail/hasallocator.hpp"
|
||||
#include <cstddef> //size_t, ptrdiff_t
|
||||
#include <iterator> //reverse_iterator
|
||||
|
||||
#include "storage_for.hpp"
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T, class Allocator = allocator<T>>
|
||||
class buffer : protected detail::hasallocator<Allocator>
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using allocator_type = Allocator;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
protected:
|
||||
pointer m_data = nullptr;
|
||||
size_type m_cap = 0;
|
||||
size_type m_size = 0;
|
||||
|
||||
public:
|
||||
constexpr buffer(void);
|
||||
REXY_CPP20_CONSTEXPR buffer(const_pointer data, size_type length)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||
template<class Iter>
|
||||
REXY_CPP20_CONSTEXPR buffer(const Iter& start, const Iter& last);
|
||||
REXY_CPP20_CONSTEXPR buffer(size_type cap)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||
REXY_CPP20_CONSTEXPR buffer(const buffer& b)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||
constexpr buffer(buffer&& b)noexcept;
|
||||
REXY_CPP20_CONSTEXPR ~buffer(void)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR buffer& operator=(const buffer& b)
|
||||
noexcept(is_nothrow_allocator_v<Allocator>);
|
||||
constexpr buffer& operator=(buffer&& b)noexcept;
|
||||
|
||||
constexpr pointer data(void);
|
||||
constexpr const_pointer data(void)const;
|
||||
REXY_CPP20_CONSTEXPR void resize(size_type new_cap);
|
||||
constexpr void set_size(size_type size);
|
||||
constexpr size_type cap(void)const;
|
||||
constexpr const size_type& size(void)const;
|
||||
constexpr size_type& size(void);
|
||||
|
||||
constexpr pointer release(void);
|
||||
|
||||
constexpr reference operator[](size_type i);
|
||||
constexpr const_reference operator[](size_type i)const;
|
||||
constexpr reference at(size_type i);
|
||||
constexpr const_reference at(size_type i)const;
|
||||
|
||||
constexpr iterator begin(void);
|
||||
constexpr const_iterator begin(void)const;
|
||||
constexpr const_iterator cbegin(void)const;
|
||||
constexpr iterator end(void);
|
||||
constexpr const_iterator end(void)const;
|
||||
constexpr const_iterator cend(void)const;
|
||||
constexpr reverse_iterator rbegin(void);
|
||||
constexpr const_reverse_iterator rbegin(void)const;
|
||||
constexpr reverse_iterator rend(void);
|
||||
constexpr const_reverse_iterator rend(void)const;
|
||||
constexpr const_reverse_iterator crbegin(void)const;
|
||||
constexpr const_reverse_iterator crend(void)const;
|
||||
|
||||
REXY_CPP20_CONSTEXPR void clear(void);
|
||||
|
||||
REXY_CPP20_CONSTEXPR void append(const_pointer p, size_type len);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "buffer.tpp"
|
||||
|
||||
#endif
|
||||
220
include/rexy/buffer.tpp
Normal file
220
include/rexy/buffer.tpp
Normal file
@ -0,0 +1,220 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2021 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BUFFER_TPP
|
||||
#define REXY_BUFFER_TPP
|
||||
|
||||
#include <utility> //exchange, swap
|
||||
#include <algorithm> //max
|
||||
#include <memory> //construct_at
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T, class Allocator>
|
||||
constexpr buffer<T,Allocator>::buffer(void){}
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const_pointer data, size_type length)noexcept(is_nothrow_allocator_v<Allocator>):
|
||||
m_data(this->allocate(sizeof(value_type) * length)),
|
||||
m_cap(length),
|
||||
m_size(length)
|
||||
{
|
||||
for(size_type i = 0;i < length;++i){
|
||||
std::construct_at(m_data + i, data[i]);
|
||||
}
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
template<class Iter>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const Iter& start, const Iter& last){
|
||||
size_type count = 0;
|
||||
for(auto it = start;it != last;++it){
|
||||
++count;
|
||||
}
|
||||
m_data = this->allocate(sizeof(value_type) * count);
|
||||
m_cap = count;
|
||||
|
||||
count = 0;
|
||||
for(auto it = start;it != last;++it){
|
||||
std::construct_at(m_data + count, *it);
|
||||
++count;
|
||||
}
|
||||
m_size = count;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(size_type cap)noexcept(is_nothrow_allocator_v<Allocator>):
|
||||
m_data(this->allocate(sizeof(value_type) * cap)),
|
||||
m_cap(cap),
|
||||
m_size(0){}
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const buffer& b)noexcept(is_nothrow_allocator_v<Allocator>):
|
||||
m_data(this->allocate(sizeof(value_type) * b.m_cap)),
|
||||
m_cap(b.m_cap),
|
||||
m_size(b.m_size)
|
||||
{
|
||||
for(size_type i = 0;i < b.m_size;++i){
|
||||
std::construct_at(m_data + i, b.m_data[i]);
|
||||
}
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr buffer<T,Allocator>::buffer(buffer&& b)noexcept:
|
||||
m_data(std::exchange(b.m_data, nullptr)),
|
||||
m_cap(b.m_cap),
|
||||
m_size(b.m_size){}
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::~buffer(void)noexcept(is_nothrow_allocator_v<Allocator>){
|
||||
for(size_type i = 0;i < m_size;++i){
|
||||
std::destroy_at(m_data + i);
|
||||
}
|
||||
this->deallocate(m_data, m_cap * sizeof(value_type));
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>& buffer<T,Allocator>::operator=(const buffer& b)
|
||||
noexcept(is_nothrow_allocator_v<Allocator>)
|
||||
{
|
||||
return (*this = buffer(b));
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr buffer<T,Allocator>& buffer<T,Allocator>::operator=(buffer&& b)noexcept{
|
||||
std::swap(m_data, b.m_data);
|
||||
std::swap(m_size, b.m_size);
|
||||
std::swap(m_cap, b.m_cap);
|
||||
return *this;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::data(void) -> pointer{
|
||||
return m_data;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::data(void)const -> const_pointer{
|
||||
return m_data;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR void buffer<T,Allocator>::resize(size_type new_cap){
|
||||
if(new_cap > m_cap){
|
||||
buffer tmp(new_cap);
|
||||
for(size_type i = 0;i < m_size;++i){
|
||||
std::construct_at(tmp.m_data + i, std::move(m_data[i]));
|
||||
}
|
||||
std::swap(tmp.m_data, m_data);
|
||||
}
|
||||
m_cap = new_cap;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr void buffer<T,Allocator>::set_size(size_type size){
|
||||
m_size = size;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::cap(void)const -> size_type{
|
||||
return m_cap;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::size(void)const -> const size_type&{
|
||||
return m_size;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::size(void) -> size_type&{
|
||||
return m_size;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::release(void) -> pointer{
|
||||
return std::exchange(m_data, nullptr);
|
||||
}
|
||||
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::operator[](size_type i) -> reference{
|
||||
return m_data[i];
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::operator[](size_type i)const -> const_reference{
|
||||
return m_data[i];
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::at(size_type i) -> reference{
|
||||
return m_data[i];
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::at(size_type i)const -> const_reference{
|
||||
return m_data[i];
|
||||
}
|
||||
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::begin(void) -> iterator{
|
||||
return m_data;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::begin(void)const -> const_iterator{
|
||||
return m_data;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::cbegin(void)const -> const_iterator{
|
||||
return m_data;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::end(void) -> iterator{
|
||||
return m_data + m_size;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::end(void)const -> const_iterator{
|
||||
return m_data + m_size;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::cend(void)const -> const_iterator{
|
||||
return m_data + m_size;
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::rbegin(void) -> reverse_iterator{
|
||||
return reverse_iterator(m_data + m_size);
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::rbegin(void)const -> const_reverse_iterator{
|
||||
return const_reverse_iterator(m_data + m_size);
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::rend(void) -> reverse_iterator{
|
||||
return reverse_iterator(m_data - 1);
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::rend(void)const -> const_reverse_iterator{
|
||||
return const_reverse_iterator(m_data - 1);
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::crbegin(void)const -> const_reverse_iterator{
|
||||
return const_reverse_iterator(m_data + m_size);
|
||||
}
|
||||
template<class T, class Allocator>
|
||||
constexpr auto buffer<T,Allocator>::crend(void)const -> const_reverse_iterator{
|
||||
return const_reverse_iterator(m_data - 1);
|
||||
}
|
||||
|
||||
template<class T, class Allocator>
|
||||
REXY_CPP20_CONSTEXPR void buffer<T,Allocator>::append(const_pointer p, size_type len){
|
||||
if(len + m_size > m_cap){
|
||||
buffer b(std::max(m_cap * 2, len + m_size));
|
||||
b.append(m_data, m_size);
|
||||
b.append(p, len);
|
||||
*this = std::move(b);
|
||||
}else{
|
||||
for(size_type i = 0;i < len;++i){
|
||||
std::construct_at(m_data + (m_size + i), p[i]);
|
||||
}
|
||||
m_size += len;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
23
include/rexy/compat/allocator.hpp
Normal file
23
include/rexy/compat/allocator.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#include "cpp20/allocator.hpp"
|
||||
#else
|
||||
#include "cpp17/allocator.hpp"
|
||||
#endif
|
||||
100
include/rexy/compat/cpp17/allocator.hpp
Normal file
100
include/rexy/compat/cpp17/allocator.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP
|
||||
#define REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP
|
||||
|
||||
#include <cstddef> //ptrdiff_t, size_t
|
||||
#include <type_traits> //true_type, false_type
|
||||
#include <new>
|
||||
#include <limits> //numeric_limits
|
||||
|
||||
#include "../../rexy.hpp"
|
||||
|
||||
#define REXY_ALLOCATOR_CONCEPT class
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T>
|
||||
struct allocator
|
||||
{
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using void_pointer = void*;
|
||||
using const_void_pointer = const void*;
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using is_always_equal = std::true_type;
|
||||
using propagate_on_container_copy_assignment = std::false_type;
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using propagate_on_container_swap = std::false_type;
|
||||
|
||||
template<class U>
|
||||
struct rebind{
|
||||
using other = allocator<U>;
|
||||
};
|
||||
|
||||
private:
|
||||
constexpr bool has_overflow(size_type n)const{
|
||||
return n > max_size();
|
||||
}
|
||||
|
||||
public:
|
||||
allocator(void) = default;
|
||||
allocator(const allocator&) = default;
|
||||
allocator(allocator&&) = default;
|
||||
template<class U>
|
||||
constexpr allocator(const allocator<U>&)noexcept{}
|
||||
~allocator(void) = default;
|
||||
|
||||
pointer allocate(size_type n){
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
return reinterpret_cast<pointer>(::operator new(bytes));
|
||||
}else{
|
||||
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
|
||||
}
|
||||
}
|
||||
void deallocate(pointer p, size_type n){
|
||||
#if !defined(__cpp_sized_deallocation)
|
||||
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p);
|
||||
}else{
|
||||
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#else //__cpp_sized_deallocation
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p, bytes);
|
||||
}else{
|
||||
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#endif //__cpp_sized_deallocation
|
||||
|
||||
}
|
||||
constexpr size_type max_size(void)const{
|
||||
return std::numeric_limits<size_type>::max()/sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
44
include/rexy/compat/cpp17/cx/string.hpp
Normal file
44
include/rexy/compat/cpp17/cx/string.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP17_CX_STRING_HPP
|
||||
#define REXY_COMPAT_CPP17_CX_STRING_HPP
|
||||
|
||||
#include "../../../string_base.hpp" //string_cat_expr
|
||||
#include "../../../string_view.hpp" //string_view
|
||||
#include <utility> //forward
|
||||
#include <type_traits> //enable_if
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<class Str1, class Str2, std::enable_if_t<is_cx_string<Str1,Str2>::value,int> = 0>
|
||||
constexpr auto operator+(Str1&& l, Str2&& r)noexcept{
|
||||
return string_cat_expr(std::forward<Str1>(l), std::forward<Str2>(r));
|
||||
}
|
||||
template<class Str1, std::enable_if_t<is_cx_string<Str1>::value,int> = 0>
|
||||
constexpr auto operator+(Str1&& l, const char* r)noexcept{
|
||||
return string_cat_expr(std::forward<Str1>(l), rexy::basic_string_view<char>(r));
|
||||
}
|
||||
template<class Str1, std::enable_if_t<is_cx_string<Str1>::value,int> = 0>
|
||||
constexpr auto operator+(const char* l, Str1&& r)noexcept{
|
||||
return string_cat_expr(rexy::basic_string_view<char>(l), std::forward<Str1>(r));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
107
include/rexy/compat/cpp17/source_location.hpp
Normal file
107
include/rexy/compat/cpp17/source_location.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_17_SOURCE_LOCATION_HPP
|
||||
#define REXY_COMPAT_17_SOURCE_LOCATION_HPP
|
||||
|
||||
#include <cstdint> //uint_least32_t
|
||||
|
||||
//clang bug: https://bugs.llvm.org/show_bug.cgi?id=48886
|
||||
#if defined(__cpp_consteval) && !defined(__clang__)
|
||||
#define CONSTEVAL consteval
|
||||
#else
|
||||
#define CONSTEVAL constexpr
|
||||
#endif
|
||||
|
||||
namespace rexy::compat::cpp17{
|
||||
|
||||
class source_location
|
||||
{
|
||||
private:
|
||||
const char* m_file;
|
||||
const char* m_func;
|
||||
uint_least32_t m_line;
|
||||
uint_least32_t m_column;
|
||||
|
||||
public:
|
||||
constexpr source_location(void)noexcept;
|
||||
constexpr source_location(const source_location&);
|
||||
constexpr source_location(source_location&&)noexcept;
|
||||
|
||||
constexpr uint_least32_t line(void)const noexcept;
|
||||
constexpr uint_least32_t column(void)const noexcept;
|
||||
constexpr const char* file_name(void)const noexcept;
|
||||
constexpr const char* function_name(void)const noexcept;
|
||||
|
||||
public:
|
||||
#if (defined(__clang__) && (__clang_major__ >= 9)) || (defined(_MSC_VER) && (_MSC_VER >= 1926))
|
||||
static CONSTEVAL source_location current(uint_least32_t line = __builtin_LINE(),
|
||||
uint_least32_t col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION())noexcept;
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
||||
static CONSTEVAL source_location current(uint_least32_t line = __builtin_LINE(),
|
||||
uint_least32_t col = 0,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION())noexcept;
|
||||
#else
|
||||
static CONSTEVAL source_location current(uint_least32_t line = 0,
|
||||
uint_least32_t col = 0,
|
||||
const char* file = "unknown",
|
||||
const char* func = "unknown")noexcept;
|
||||
#endif
|
||||
|
||||
private:
|
||||
constexpr source_location(uint_least32_t line, uint_least32_t col, const char* file, const char* func)noexcept;
|
||||
};
|
||||
|
||||
constexpr source_location::source_location(void)noexcept:
|
||||
source_location(0, 0, "unknown", "unknown"){}
|
||||
constexpr source_location::source_location(const source_location& s):
|
||||
source_location(s.m_line, s.m_column, s.m_file, s.m_func){}
|
||||
constexpr source_location::source_location(source_location&& s)noexcept:
|
||||
source_location(s.m_line, s.m_column, s.m_file, s.m_func){}
|
||||
constexpr source_location::source_location(uint_least32_t line, uint_least32_t col, const char* file, const char* func)noexcept:
|
||||
m_file(file),
|
||||
m_func(func),
|
||||
m_line(line),
|
||||
m_column(col){}
|
||||
|
||||
constexpr uint_least32_t source_location::line(void)const noexcept{
|
||||
return m_line;
|
||||
}
|
||||
constexpr uint_least32_t source_location::column(void)const noexcept{
|
||||
return m_column;
|
||||
}
|
||||
constexpr const char* source_location::file_name(void)const noexcept{
|
||||
return m_file;
|
||||
}
|
||||
constexpr const char* source_location::function_name(void)const noexcept{
|
||||
return m_func;
|
||||
}
|
||||
|
||||
CONSTEVAL source_location source_location::current(uint_least32_t line, uint_least32_t col, const char* file, const char* func)noexcept{
|
||||
return source_location(line, col, file, func);
|
||||
}
|
||||
|
||||
|
||||
#undef CONSTEVAL
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
336
include/rexy/compat/cpp17/string_base.hpp
Normal file
336
include/rexy/compat/cpp17/string_base.hpp
Normal file
@ -0,0 +1,336 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP17_STRING_BASE_HPP
|
||||
#define REXY_COMPAT_CPP17_STRING_BASE_HPP
|
||||
|
||||
#include "../../string_base.hpp"
|
||||
|
||||
#include <utility> //forward
|
||||
#include <type_traits> //{false,true}_type, declval, enable_if, remove_reference, decay
|
||||
#include <algorithm> //lexicographically_compare
|
||||
|
||||
#include "../../utility.hpp" //strlen, strncmp
|
||||
#include "../../traits.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class Left, class Right>
|
||||
class string_cat_expr;
|
||||
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
|
||||
class basic_string;
|
||||
template<class Char>
|
||||
class basic_string_view;
|
||||
|
||||
#define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \
|
||||
template<class T> \
|
||||
struct has_##fun##_f{ \
|
||||
static std::false_type check(...); \
|
||||
template<class type> \
|
||||
static auto check(type* u) -> std::enable_if_t<std::is_convertible_v<decltype(std::declval<type>().fun(__VA_ARGS__)),ret>,std::true_type>; \
|
||||
\
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_reference_t<T>*>()))::value; \
|
||||
}; \
|
||||
template<class T> \
|
||||
static constexpr bool has_##fun##_f_v = has_##fun##_f<T>::value
|
||||
|
||||
#define REXY_HAS_MEMOP_WITH_RET(type, ret, opname, op, ...) \
|
||||
template<class T> \
|
||||
struct has_##opname##_f{ \
|
||||
static std::false_type check(...); \
|
||||
template<class type> \
|
||||
static auto check(type* u) -> std::enable_if_t<std::is_convertible_v<decltype(std::declval<type>().operator op(__VA_ARGS__)),ret>,std::true_type>; \
|
||||
\
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_reference_t<T>*>()))::value; \
|
||||
}; \
|
||||
template<class T> \
|
||||
static constexpr bool has_##opname##_f_v = has_##opname##_f<T>::value
|
||||
|
||||
|
||||
REXY_HAS_MEMFUN_WITH_RET(U, typename U::size_type, length);
|
||||
REXY_HAS_MEMFUN_WITH_RET(U, typename U::const_pointer, c_str);
|
||||
REXY_HAS_MEMOP_WITH_RET(U, typename U::const_reference, indexop, [], 0);
|
||||
REXY_HAS_MEMFUN_WITH_RET(U, typename U::const_iterator, begin);
|
||||
REXY_HAS_MEMFUN_WITH_RET(U, typename U::const_iterator, end);
|
||||
|
||||
#undef REXY_HAS_MEMFUN_WITH_RET
|
||||
#undef REXY_HAS_MEMOP_WITH_RET
|
||||
|
||||
template<class T>
|
||||
struct is_basic_string{
|
||||
template<class Char, class Alloc>
|
||||
static std::true_type check(const basic_string<Char,Alloc>*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<rexy::remove_cvref_t<T>*>()))::value;
|
||||
};
|
||||
template<class T>
|
||||
struct is_basic_string_view{
|
||||
template<class Char>
|
||||
static std::true_type check(const basic_string_view<Char>*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<rexy::remove_cvref_t<T>*>()))::value;
|
||||
};
|
||||
template<class T>
|
||||
struct is_basic_string_expr{
|
||||
template<class Left, class Right>
|
||||
static std::true_type check(const string_cat_expr<Left,Right>*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<rexy::remove_cvref_t<T>*>()))::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct is_string{
|
||||
static constexpr bool value = (is_basic_string<T>::value || is_basic_string_view<T>::value) &&
|
||||
(has_length_f_v<T> && has_c_str_f_v<T> && has_indexop_f_v<T> && has_begin_f_v<T> && has_end_f_v<T>);
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_string_v = is_string<T>::value;
|
||||
|
||||
template<class... Ts>
|
||||
struct are_strings{
|
||||
static constexpr bool value = (is_string<Ts>::value && ...);
|
||||
};
|
||||
template<class... Ts>
|
||||
static constexpr bool are_strings_v = are_strings<Ts...>::value;
|
||||
template<class T>
|
||||
struct is_string_expr{
|
||||
static constexpr bool value = is_basic_string_expr<T>::value;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
|
||||
template<class... Ts>
|
||||
struct are_string_expr{
|
||||
static constexpr bool value = (is_string_expr<Ts>::value && ...);
|
||||
};
|
||||
template<class... Ts>
|
||||
static constexpr bool are_string_expr_v = are_string_expr<Ts...>::value;
|
||||
|
||||
//Compare
|
||||
template<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
|
||||
constexpr bool operator==(const Str1& left, const Str2& right){
|
||||
if(left.length() != right.length()){
|
||||
return false;
|
||||
}
|
||||
return !rexy::strncmp(left.c_str(), right.c_str(), left.length()+1);
|
||||
}
|
||||
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
||||
constexpr bool operator==(const Str1& left, typename std::decay_t<Str1>::const_pointer right){
|
||||
if(right == nullptr){
|
||||
return false;
|
||||
}
|
||||
const rexy::basic_string_view rstr(right);
|
||||
if(rstr.length() != left.length()){
|
||||
return false;
|
||||
}
|
||||
return !rexy::strncmp(left.c_str(), rstr.c_str(), left.length());
|
||||
}
|
||||
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
||||
constexpr bool operator==(typename std::decay_t<Str1>::const_pointer left, const Str1& right){
|
||||
if(left == nullptr){
|
||||
return false;
|
||||
}
|
||||
const rexy::basic_string_view lstr(left);
|
||||
if(lstr.length() != right.length()){
|
||||
return false;
|
||||
}
|
||||
return !rexy::strncmp(lstr.c_str(), right.c_str(), right.length());
|
||||
}
|
||||
template<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
|
||||
constexpr bool operator!=(const Str1& left, const Str2& right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
||||
constexpr bool operator!=(const Str1& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
||||
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, const Str1& right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
||||
constexpr bool operator<(const Str1& left, const Str2& right)noexcept{
|
||||
return std::lexicographical_compare(left.begin(), left.end(), right.begin(), right.end());
|
||||
}
|
||||
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
||||
constexpr bool operator<=(const Str1& left, const Str2& right)noexcept{
|
||||
return !(right < left);
|
||||
}
|
||||
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
||||
constexpr bool operator>(const Str1& left, const Str2& right)noexcept{
|
||||
return (right < left);
|
||||
}
|
||||
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
||||
constexpr bool operator>=(const Str1& left, const Str2& right)noexcept{
|
||||
return !(left < right);
|
||||
}
|
||||
|
||||
//String + string concat
|
||||
template<class Left, class Right, std::enable_if_t<are_strings<Left, Right>::value,int> = 0>
|
||||
constexpr auto operator+(Left&& l, Right&& r)
|
||||
//uses deduction guide whereas std::is_nothrow_constructible couldn't
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(l), std::forward<Right>(r))))
|
||||
{
|
||||
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
|
||||
}
|
||||
//String + char pointer
|
||||
template<class Right, std::enable_if_t<is_string<Right>::value,int> = 0>
|
||||
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right))))
|
||||
{
|
||||
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
|
||||
}
|
||||
//Char pointer + string
|
||||
template<class Left, std::enable_if_t<is_string<Left>::value,int> = 0>
|
||||
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right))))
|
||||
{
|
||||
return rexy::string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
|
||||
}
|
||||
|
||||
|
||||
//String + expr concat
|
||||
template<class Left, class Right, std::enable_if_t<is_string<Left>::value && is_string_expr<Right>::value,int> = 0>
|
||||
constexpr auto operator+(Left&& left, Right&& right){
|
||||
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
|
||||
}
|
||||
//Expr + string
|
||||
template<class Left, class Right, std::enable_if_t<is_string_expr<Left>::value && is_string<Right>::value,int> = 0>
|
||||
constexpr auto operator+(Left&& left, Right&& right){
|
||||
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
|
||||
}
|
||||
//Expr + expr
|
||||
template<class Left, class Right, std::enable_if_t<are_string_expr<Left,Right>::value,int> = 0>
|
||||
constexpr auto operator+(Left&& left, Right&& right){
|
||||
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
|
||||
}
|
||||
//Expr + char pointer
|
||||
template<class Left, std::enable_if_t<is_string_expr<Left>::value,int> = 0>
|
||||
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right){
|
||||
return string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
|
||||
}
|
||||
//char pointer + Expr
|
||||
template<class Right, std::enable_if_t<is_string_expr<Right>::value,int> = 0>
|
||||
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right){
|
||||
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
|
||||
}
|
||||
|
||||
//String concat assignment
|
||||
template<class Left, class Right, std::enable_if_t<are_strings<Left,Right>::value,int> = 0>
|
||||
decltype(auto) operator+=(Left& l, Right&& r)
|
||||
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
|
||||
{
|
||||
return l = (l + std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, class Right, std::enable_if_t<is_string<Left>::value && is_string_expr<Right>::value,int> = 0>
|
||||
decltype(auto) operator+=(Left& l, Right&& r)
|
||||
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
|
||||
{
|
||||
return l = (l + std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, std::enable_if_t<is_string<Left>::value, int> = 0>
|
||||
decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
|
||||
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
|
||||
{
|
||||
return l = (l + r);
|
||||
}
|
||||
|
||||
template<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
|
||||
constexpr std::size_t find_first_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
for(auto it = t.cbegin() + start;it != t.cend();++it){
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
template<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
|
||||
constexpr std::size_t find_first_not_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
for(auto it = t.cbegin() + start;it != t.cend();++it){
|
||||
bool found = false;
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
template<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
|
||||
constexpr std::size_t find_last_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
const auto b = t.cend() - 1;
|
||||
const auto e = t.cbegin() - 1 - start;
|
||||
|
||||
for(auto it = b;it != e;--it){
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
template<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
|
||||
constexpr std::size_t find_last_not_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
const auto b = t.cend() - 1;
|
||||
const auto e = t.cbegin() - 1 - start;
|
||||
|
||||
|
||||
for(auto it = b;it != e;--it){
|
||||
bool found = false;
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,16 +16,19 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_CX_CX_STRING_HASH_HPP
|
||||
#define REXY_CX_CX_STRING_HASH_HPP
|
||||
#ifndef REXY_COMPAT_17_TO_ADDRESS_HPP
|
||||
#define REXY_COMPAT_17_TO_ADDRESS_HPP
|
||||
|
||||
#include "../string_hash.hpp"
|
||||
#include "string.hpp"
|
||||
namespace rexy::compat::cpp17{
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<size_t N>
|
||||
struct hash<rexy::cx::string<N>> : public string_hash<rexy::cx::string<N>>{};
|
||||
template<class T>
|
||||
constexpr T* to_address(T* p)noexcept{
|
||||
return p;
|
||||
}
|
||||
template<class Ptr>
|
||||
constexpr auto to_address(const Ptr& p)noexcept{
|
||||
return to_address(p.operator->());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
73
include/rexy/compat/cpp17/traits.hpp
Normal file
73
include/rexy/compat/cpp17/traits.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP17_TRAITS_HPP
|
||||
#define REXY_COMPAT_CPP17_TRAITS_HPP
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T>
|
||||
struct remove_volatile{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
struct remove_volatile<volatile T>{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
using remove_volatile_t = typename remove_volatile<T>::type;
|
||||
template<class T>
|
||||
struct remove_const{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
struct remove_const<const T>{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
using remove_const_t = typename remove_const<T>::type;
|
||||
template<class T>
|
||||
struct remove_cv{
|
||||
using type = remove_volatile_t<remove_const_t<T>>;
|
||||
};
|
||||
template<class T>
|
||||
using remove_cv_t = typename remove_cv<T>::type;
|
||||
template<class T>
|
||||
struct remove_reference{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
struct remove_reference<T&>{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
struct remove_reference<T&&>{
|
||||
using type = T;
|
||||
};
|
||||
template<class T>
|
||||
using remove_reference_t = typename remove_reference<T>::type;
|
||||
template<class T>
|
||||
struct remove_cvref{
|
||||
using type = remove_cv_t<remove_reference_t<T>>;
|
||||
};
|
||||
template<class T>
|
||||
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
139
include/rexy/compat/cpp20/allocator.hpp
Normal file
139
include/rexy/compat/cpp20/allocator.hpp
Normal file
@ -0,0 +1,139 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP
|
||||
#define REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP
|
||||
|
||||
#include <cstddef> //ptrdiff_t, size_t
|
||||
#include <type_traits> //true_type, false_type
|
||||
#include <new>
|
||||
#include <limits> //numeric_limits
|
||||
#include <memory> //allocator
|
||||
|
||||
#ifdef __cpp_concepts
|
||||
|
||||
#include <concepts> //convertible_to
|
||||
template<class T>
|
||||
concept Allocator = requires(T a, typename T::size_type sz, typename T::pointer pa){
|
||||
typename T::value_type;
|
||||
typename T::size_type;
|
||||
typename T::pointer;
|
||||
typename T::const_pointer;
|
||||
{a.allocate(sz)} -> std::convertible_to<typename T::pointer>;
|
||||
{a.deallocate(pa, sz)};
|
||||
};
|
||||
#define REXY_ALLOCATOR_CONCEPT Allocator
|
||||
|
||||
#else //__cpp_concepts
|
||||
|
||||
#define REXY_ALLOCATOR_CONCEPT class
|
||||
|
||||
#endif // __cpp_concepts
|
||||
|
||||
#include "../../rexy.hpp"
|
||||
#include "../if_consteval.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T>
|
||||
struct allocator
|
||||
{
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using void_pointer = void*;
|
||||
using const_void_pointer = const void*;
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using is_always_equal = std::true_type;
|
||||
using propagate_on_container_copy_assignment = std::false_type;
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using propagate_on_container_swap = std::false_type;
|
||||
|
||||
template<class U>
|
||||
struct rebind{
|
||||
using other = allocator<U>;
|
||||
};
|
||||
|
||||
private:
|
||||
constexpr bool has_overflow(size_type n)const{
|
||||
return n > max_size();
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr allocator(void) = default;
|
||||
constexpr allocator(const allocator&) = default;
|
||||
constexpr allocator(allocator&&) = default;
|
||||
template<class U>
|
||||
constexpr allocator(const allocator<U>&)noexcept{}
|
||||
constexpr ~allocator(void) = default;
|
||||
|
||||
//'::operator new' is never a constexpr call as of C++23. However, 'std::allocator::allocate' *is* transiently constexpr as of C++20,
|
||||
//even though it directly calls '::operator new' as is stated in the standard. Therefore, when evaluating this call with support for
|
||||
//'if consteval', we can use the 'std::allocator::allocate' constexpr-ness when this is in a constant evaluation context.
|
||||
constexpr pointer allocate(size_type n){
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
|
||||
REXY_if_consteval{
|
||||
std::allocator<value_type> a;
|
||||
const size_type num_items = n / sizeof(value_type);
|
||||
return a.allocate(num_items);
|
||||
}else{
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
return reinterpret_cast<pointer>(::operator new(bytes));
|
||||
}else{
|
||||
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
|
||||
}
|
||||
} //if consteval
|
||||
}
|
||||
|
||||
|
||||
constexpr void deallocate(pointer p, size_type n){
|
||||
REXY_if_consteval{
|
||||
std::allocator<value_type> a;
|
||||
const size_type num_items = n / sizeof(value_type);
|
||||
return a.deallocate(p, num_items);
|
||||
}else{
|
||||
|
||||
#if !defined(__cpp_sized_deallocation)
|
||||
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p);
|
||||
}else{
|
||||
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#else //__cpp_sized_deallocation
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p, bytes);
|
||||
}else{
|
||||
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#endif //__cpp_sized_deallocation
|
||||
|
||||
} //if consteval
|
||||
|
||||
}
|
||||
constexpr size_type max_size(void)const{
|
||||
return std::numeric_limits<size_type>::max()/sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
46
include/rexy/compat/cpp20/cx/string.hpp
Normal file
46
include/rexy/compat/cpp20/cx/string.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP20_CX_STRING_HPP
|
||||
#define REXY_COMPAT_CPP20_CX_STRING_HPP
|
||||
|
||||
#include "../../../string_base.hpp" //string_cat_expr
|
||||
#include "../../../string_view.hpp" //string_view
|
||||
#include <utility> //forward
|
||||
|
||||
namespace rexy{
|
||||
template<class T>
|
||||
concept CxString = cx::is_cx_string<T>::value;
|
||||
}
|
||||
namespace rexy::cx{
|
||||
template<CxString Str1, CxString Str2>
|
||||
constexpr auto operator+(Str1&& l, Str2&& r)noexcept{
|
||||
return string_cat_expr(std::forward<Str1>(l), std::forward<Str2>(r));
|
||||
}
|
||||
template<CxString Str1>
|
||||
constexpr auto operator+(Str1&& l, const char* r)noexcept{
|
||||
return string_cat_expr(std::forward<Str1>(l), rexy::basic_string_view<char>(r));
|
||||
}
|
||||
template<CxString Str1>
|
||||
constexpr auto operator+(const char* l, Str1&& r)noexcept{
|
||||
return string_cat_expr(rexy::basic_string_view<char>(l), std::forward<Str1>(r));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,18 +16,14 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BINARY_HPP
|
||||
#define REXY_BINARY_HPP
|
||||
#ifndef REXY_COMPAT_20_SOURCE_LOCATION_HPP
|
||||
#define REXY_COMPAT_20_SOURCE_LOCATION_HPP
|
||||
|
||||
#include "binary_base.hpp"
|
||||
#include "allocator.hpp"
|
||||
#include "rexy.hpp"
|
||||
#include <source_location>
|
||||
|
||||
namespace rexy{
|
||||
namespace rexy::compat::cpp20{
|
||||
|
||||
using binary = basic_binary<allocator<char>>;
|
||||
|
||||
extern template class basic_binary<allocator<char>>;
|
||||
using source_location = std::source_location;
|
||||
|
||||
}
|
||||
|
||||
257
include/rexy/compat/cpp20/string_base.hpp
Normal file
257
include/rexy/compat/cpp20/string_base.hpp
Normal file
@ -0,0 +1,257 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP20_STRING_BASE_HPP
|
||||
#define REXY_COMPAT_CPP20_STRING_BASE_HPP
|
||||
|
||||
#include "../../string_base.hpp"
|
||||
|
||||
#include <utility> //forward
|
||||
#include <type_traits> //decay, is_nothrow_assignable
|
||||
#include <algorithm> //lexicographically_compare_three_way
|
||||
|
||||
#include "../../utility.hpp" //strlen, strncmp
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class Left, class Right>
|
||||
class string_cat_expr;
|
||||
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
|
||||
class basic_string;
|
||||
template<class Char>
|
||||
class basic_string_view;
|
||||
|
||||
template<class T>
|
||||
struct is_basic_string{
|
||||
template<class Char, class Alloc>
|
||||
static std::true_type check(const basic_string<Char,Alloc>*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct is_basic_string_view{
|
||||
template<class Char>
|
||||
static std::true_type check(const basic_string_view<Char>*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct is_basic_string_expr{
|
||||
template<class Left, class Right>
|
||||
static std::true_type check(const string_cat_expr<Left,Right>*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
concept BasicString = requires(const T& a){
|
||||
requires(is_basic_string<T>::value || is_basic_string_view<T>::value);
|
||||
{std::as_const(a).length()} -> std::convertible_to<typename std::decay_t<T>::size_type>;
|
||||
{std::as_const(a).c_str()} -> std::convertible_to<typename std::decay_t<T>::const_pointer>;
|
||||
{std::as_const(a)[0]} -> std::convertible_to<typename std::decay_t<T>::const_reference>;
|
||||
{std::as_const(a).begin()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
|
||||
{std::as_const(a).end()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
|
||||
};
|
||||
template<class T>
|
||||
concept StringExpr = is_basic_string_expr<T>::value;
|
||||
template<class T>
|
||||
concept String = BasicString<T> || StringExpr<T>;
|
||||
|
||||
template<class T>
|
||||
struct is_string{
|
||||
static constexpr bool value = BasicString<T>;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_string_v = is_string<T>::value;
|
||||
template<class T>
|
||||
struct is_string_expr{
|
||||
static constexpr bool value = StringExpr<T>;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
|
||||
|
||||
//Compare
|
||||
template<BasicString Str1, BasicString Str2>
|
||||
constexpr bool operator==(const Str1& left, const Str2& right){
|
||||
if(left.length() != right.length()){
|
||||
return false;
|
||||
}
|
||||
return !rexy::strncmp(left.c_str(), right.c_str(), left.length()+1);
|
||||
}
|
||||
template<BasicString Str1>
|
||||
constexpr bool operator==(const Str1& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
|
||||
if(right == nullptr){
|
||||
return false;
|
||||
}
|
||||
const rexy::basic_string_view rstr(right);
|
||||
if(rstr.length() != left.length()){
|
||||
return false;
|
||||
}
|
||||
return !rexy::strncmp(left.c_str(), rstr.c_str(), left.length());
|
||||
}
|
||||
template<BasicString Str1>
|
||||
constexpr bool operator==(typename std::decay_t<Str1>::const_pointer left, const Str1& right)noexcept{
|
||||
if(left == nullptr){
|
||||
return false;
|
||||
}
|
||||
const rexy::basic_string_view lstr(left);
|
||||
if(lstr.length() != right.length()){
|
||||
return false;
|
||||
}
|
||||
return !rexy::strncmp(lstr.c_str(), right.c_str(), right.length());
|
||||
}
|
||||
template<BasicString Str1, BasicString Str2>
|
||||
constexpr bool operator!=(const Str1& left, const Str2& right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
template<BasicString Str1>
|
||||
constexpr bool operator!=(const Str1& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
template<BasicString Str1>
|
||||
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, const Str1& right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
template<BasicString Str1, BasicString Str2>
|
||||
constexpr auto operator<=>(const Str1& left, const Str2& right)noexcept{
|
||||
return std::lexicographical_compare_three_way(left.begin(), left.end(), right.begin(), right.end());
|
||||
}
|
||||
|
||||
//String + string concat
|
||||
template<String Left, String Right>
|
||||
constexpr auto operator+(Left&& l, Right&& r)
|
||||
//uses deduction guide whereas std::is_nothrow_constructible couldn't
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(l), std::forward<Right>(r))))
|
||||
{
|
||||
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
|
||||
}
|
||||
template<String Right>
|
||||
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right))))
|
||||
{
|
||||
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
|
||||
}
|
||||
template<String Left>
|
||||
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right))))
|
||||
{
|
||||
return rexy::string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
|
||||
}
|
||||
|
||||
//String concat assign
|
||||
template<BasicString Left, String Right>
|
||||
constexpr decltype(auto) operator+=(Left& l, Right&& r)
|
||||
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
|
||||
{
|
||||
return l = (l + std::forward<Right>(r));
|
||||
}
|
||||
template<BasicString Left>
|
||||
constexpr decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
|
||||
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
|
||||
{
|
||||
return l = (l + r);
|
||||
}
|
||||
|
||||
template<BasicString T, class Char>
|
||||
constexpr std::size_t find_first_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
for(auto it = t.cbegin() + start;it != t.cend();++it){
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
template<BasicString T, class Char>
|
||||
constexpr std::size_t find_first_not_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
for(auto it = t.cbegin() + start;it != t.cend();++it){
|
||||
bool found = false;
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
template<BasicString T, class Char>
|
||||
constexpr std::size_t find_last_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
const auto b = t.cend() - 1;
|
||||
const auto e = t.cbegin() - 1 - start;
|
||||
|
||||
for(auto it = b;it != e;--it){
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
template<BasicString T, class Char>
|
||||
constexpr std::size_t find_last_not_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
|
||||
if(start > t.length()){
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
const auto b = t.cend() - 1;
|
||||
const auto e = t.cbegin() - 1 - start;
|
||||
|
||||
|
||||
for(auto it = b;it != e;--it){
|
||||
bool found = false;
|
||||
for(std::size_t i = 0;i < size;++i){
|
||||
if(*it == c[i]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
return it - t.cbegin();
|
||||
}
|
||||
}
|
||||
return rexy::npos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
37
include/rexy/compat/cpp20/to_address.hpp
Normal file
37
include/rexy/compat/cpp20/to_address.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_20_TO_ADDRESS_HPP
|
||||
#define REXY_COMPAT_20_TO_ADDRESS_HPP
|
||||
|
||||
#include <memory> //to_address
|
||||
|
||||
namespace rexy::compat::cpp20{
|
||||
|
||||
template<class T>
|
||||
constexpr T* to_address(T* p)noexcept{
|
||||
return std::to_address(p);
|
||||
}
|
||||
template<class Ptr>
|
||||
constexpr auto to_address(const Ptr& p)noexcept{
|
||||
return std::to_address(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,17 +16,17 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_STATIC_STRING_HASH_HPP
|
||||
#define REXY_STATIC_STRING_HASH_HPP
|
||||
#ifndef REXY_COMPAT_CPP17_TRAITS_HPP
|
||||
#define REXY_COMPAT_CPP17_TRAITS_HPP
|
||||
|
||||
#include "string_hash.hpp"
|
||||
#include "string_base.hpp"
|
||||
#include "rexy.hpp"
|
||||
#include <type_traits> //remove_cvref
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class Char>
|
||||
struct hash<rexy::static_string<Char>> : public string_hash<rexy::static_string<Char>>{};
|
||||
template<class T>
|
||||
struct remove_cvref : public std::remove_cvref<T>{};
|
||||
template<class T>
|
||||
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
|
||||
}
|
||||
|
||||
30
include/rexy/compat/if_consteval.hpp
Normal file
30
include/rexy/compat/if_consteval.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_if_consteval
|
||||
|
||||
#if defined(__cpp_if_consteval)
|
||||
#define REXY_if_consteval if consteval
|
||||
#define REXY_if_not_consteval if not consteval
|
||||
#elif defined(__cpp_lib_is_constant_evaluated)
|
||||
#include <type_traits> //is_constant_evaluated
|
||||
#define REXY_if_consteval if(std::is_constant_evaluated())
|
||||
#define REXY_if_not_consteval if(!std::is_constant_evaluated())
|
||||
#endif //__cpp_if_consteval
|
||||
|
||||
#endif
|
||||
48
include/rexy/compat/source_location.hpp
Normal file
48
include/rexy/compat/source_location.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_UTIL_SOURCE_LOCATION_HPP
|
||||
#define REXY_UTIL_SOURCE_LOCATION_HPP
|
||||
|
||||
#include <version> //feature test macro
|
||||
|
||||
#ifndef __cpp_lib_source_location
|
||||
|
||||
#include "cpp17/source_location.hpp"
|
||||
|
||||
namespace rexy::compat{
|
||||
|
||||
using source_location = cpp17::source_location;
|
||||
|
||||
}
|
||||
|
||||
#else //__cpp_lib_source_location
|
||||
|
||||
#include "cpp20/source_location.hpp"
|
||||
|
||||
namespace rexy::compat{
|
||||
|
||||
using source_location = cpp20::source_location;
|
||||
|
||||
}
|
||||
|
||||
#endif //__cpp_lib_source_location
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
45
include/rexy/compat/standard.hpp
Normal file
45
include/rexy/compat/standard.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_CPP20_HPP
|
||||
#define REXY_COMPAT_CPP20_HPP
|
||||
|
||||
#define REXY_REQUIRES_CPP20 static_assert(__cplusplus >= 202002L, "C++20 is required to use this file.")
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#define REXY_CPP20_CONSTEXPR constexpr
|
||||
#define REXY_CPP20_CONSTEVAL consteval
|
||||
#else //__cpp_consteval
|
||||
#define REXY_CPP20_CONSTEXPR
|
||||
#define REXY_CPP20_CONSTEVAL constexpr
|
||||
#endif //__cpp_consteval
|
||||
|
||||
#if __cplusplus >= 202300L
|
||||
#define REXY_STANDARD_CPP23
|
||||
#endif
|
||||
#if __cplusplus >= 202002L
|
||||
#define REXY_STANDARD_CPP20
|
||||
#endif
|
||||
#if __cplusplus >= 201703L
|
||||
#define REXY_STANDARD_CPP17
|
||||
#endif
|
||||
#if __cplusplus < 201703L
|
||||
#error "Requires minimum C++17 standard"
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
34
include/rexy/compat/string_base.hpp
Normal file
34
include/rexy/compat/string_base.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_STRING_BASE_HPP
|
||||
#define REXY_COMPAT_STRING_BASE_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy{
|
||||
static constexpr std::size_t npos = std::size_t(-1);
|
||||
}
|
||||
|
||||
#ifdef __cpp_concepts
|
||||
#include "cpp20/string_base.hpp"
|
||||
#else
|
||||
#include "cpp17/string_base.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
43
include/rexy/compat/to_address.hpp
Normal file
43
include/rexy/compat/to_address.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_TO_ADDRESS_HPP
|
||||
#define REXY_COMPAT_TO_ADDRESS_HPP
|
||||
|
||||
#ifdef __cpp_lib_to_address
|
||||
|
||||
#include "cpp20/to_address.hpp"
|
||||
namespace rexy::compat{
|
||||
|
||||
using cpp20::to_address;
|
||||
|
||||
}
|
||||
|
||||
#else //__cpp_lib_to_address
|
||||
|
||||
#include "cpp17/to_address.hpp"
|
||||
namespace rexy::compat{
|
||||
|
||||
using cpp17::to_address;
|
||||
|
||||
}
|
||||
|
||||
#endif //__cpp_lib_to_address
|
||||
|
||||
|
||||
#endif
|
||||
29
include/rexy/compat/traits.hpp
Normal file
29
include/rexy/compat/traits.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_COMPAT_TRAITS_HPP
|
||||
#define REXY_COMPAT_TRAITS_HPP
|
||||
|
||||
#ifdef REXY_STANDARD_CPP20
|
||||
#include "cpp20/traits.hpp"
|
||||
#else // REXY_STANDARD_CPP26
|
||||
#include "cpp17/traits.hpp"
|
||||
#endif // REXY_STANDARD_CPP26
|
||||
|
||||
|
||||
#endif
|
||||
@ -27,12 +27,12 @@
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<class T, size_t N>
|
||||
template<class T, std::size_t N>
|
||||
class array
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
@ -125,7 +125,7 @@ namespace rexy::cx{
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
template<std::size_t N>
|
||||
class array<bool,N> : public detail::bool_specialize_base
|
||||
{
|
||||
public:
|
||||
|
||||
@ -23,13 +23,13 @@
|
||||
#include <utility> //move, forward, pair
|
||||
#include <climits> //CHAR_BIT
|
||||
#include "../../utility.hpp" //swap
|
||||
|
||||
#include "../../compat/standard.hpp"
|
||||
|
||||
namespace rexy::cx::detail{
|
||||
class bool_specialize_base
|
||||
{
|
||||
protected:
|
||||
using size_type = size_t;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
static constexpr size_type bits_per_byte = CHAR_BIT;
|
||||
|
||||
@ -59,7 +59,7 @@ namespace rexy::cx::detail{
|
||||
m_offset(offset){}
|
||||
constexpr boolean(const boolean&)noexcept = default;
|
||||
constexpr boolean(boolean&&)noexcept = default;
|
||||
~boolean(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~boolean(void)noexcept = default;
|
||||
|
||||
constexpr boolean& operator=(const boolean& b)noexcept{
|
||||
return *this = static_cast<bool>(b);
|
||||
@ -86,15 +86,15 @@ namespace rexy::cx::detail{
|
||||
constexpr booleans(void)noexcept = default;
|
||||
constexpr booleans(const booleans&)noexcept = default;
|
||||
constexpr booleans(booleans&&)noexcept = default;
|
||||
~booleans(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~booleans(void)noexcept = default;
|
||||
|
||||
constexpr booleans& operator=(const booleans&)noexcept = default;
|
||||
constexpr booleans& operator=(booleans&&)noexcept = default;
|
||||
|
||||
constexpr boolean operator[](size_t i){
|
||||
constexpr boolean operator[](std::size_t i){
|
||||
return boolean{m_value, i};
|
||||
}
|
||||
constexpr boolean operator[](size_t i)const{
|
||||
constexpr boolean operator[](std::size_t i)const{
|
||||
return boolean{const_cast<uchar&>(m_value), i};
|
||||
}
|
||||
constexpr uchar& data(void){
|
||||
@ -121,7 +121,7 @@ namespace rexy::cx::detail{
|
||||
m_offset(offset){}
|
||||
constexpr bool_iter(const bool_iter&)noexcept = default;
|
||||
constexpr bool_iter(bool_iter&&)noexcept = default;
|
||||
~bool_iter(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~bool_iter(void)noexcept = default;
|
||||
|
||||
constexpr bool_iter& operator=(const bool_iter&)noexcept = default;
|
||||
constexpr bool_iter& operator=(bool_iter&&)noexcept = default;
|
||||
@ -209,7 +209,7 @@ namespace rexy::cx::detail{
|
||||
m_offset(b.m_offset){}
|
||||
constexpr const_bool_iter(const const_bool_iter&)noexcept = default;
|
||||
constexpr const_bool_iter(const_bool_iter&&)noexcept = default;
|
||||
~const_bool_iter(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~const_bool_iter(void)noexcept = default;
|
||||
|
||||
constexpr const_bool_iter& operator=(const const_bool_iter&)noexcept = default;
|
||||
constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -25,8 +25,7 @@
|
||||
#include "../hash.hpp"
|
||||
|
||||
#include <climits> //CHAR_BIT
|
||||
#include <cstddef> //size_t, ptrdiff_t
|
||||
#include <utility> //pair
|
||||
#include <cstddef> //std::size_t, ptrdiff_t
|
||||
#include <type_traits> //decay
|
||||
#include <initializer_list>
|
||||
|
||||
@ -43,14 +42,14 @@ namespace rexy::cx{
|
||||
template<class Key, class Value>
|
||||
element(Key,Value) -> element<Key,Value>;
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash = hash<Key>>
|
||||
template<class Key, class Value, std::size_t N, class Hash = hash<Key>>
|
||||
class hashmap
|
||||
{
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = Value;
|
||||
using value_type = element<Key,Value>;
|
||||
using size_type = size_t;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using hasher = Hash;
|
||||
using reference = mapped_type&;
|
||||
@ -65,16 +64,15 @@ namespace rexy::cx{
|
||||
|
||||
|
||||
private:
|
||||
array<mapped_type,N> m_values; //perfect hash table
|
||||
array<value_type,N> m_elements; //perfect hash table
|
||||
array<size_type,N> m_g; //'salt' values for indexing into the perfect hash table
|
||||
array<size_type,N> m_key_hashes; //full hash values for keys to verify good index values
|
||||
|
||||
public:
|
||||
constexpr hashmap(const value_type(&elements)[N])
|
||||
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
|
||||
std::is_nothrow_copy_constructible<value_type>::value &&
|
||||
std::is_nothrow_move_assignable<mapped_type>::value &&
|
||||
std::is_nothrow_invocable<Hash,Key,size_t>::value);
|
||||
std::is_nothrow_invocable<Hash,Key,std::size_t>::value);
|
||||
|
||||
//no key checks. give a correct key or get a random answer :)
|
||||
template<class U, class UHash = hash<std::decay_t<U>>>
|
||||
@ -83,123 +81,12 @@ namespace rexy::cx{
|
||||
constexpr const_reference operator[](U&& u)const noexcept;
|
||||
|
||||
template<class U, class UHash = hash<std::decay_t<U>>>
|
||||
constexpr bool is_valid(U&& u)const noexcept;
|
||||
constexpr bool contains(U&& u)const noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N])
|
||||
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
|
||||
std::is_nothrow_copy_constructible<value_type>::value &&
|
||||
std::is_nothrow_move_assignable<mapped_type>::value &&
|
||||
std::is_nothrow_invocable<Hash,Key,size_t>::value)
|
||||
{
|
||||
array<vector<value_type,N>,N> buckets;
|
||||
size_type current_bucket = 0;
|
||||
|
||||
//place all keys into buckets
|
||||
for(auto& element : elements){
|
||||
buckets[Hash{}(element.key, 0) % max_size].push_back(element);
|
||||
}
|
||||
|
||||
//sort the buckets based on size, largest first
|
||||
quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{
|
||||
return left.size() > right.size();
|
||||
});
|
||||
|
||||
//for each bucket, try different values of 'd' to try to find a hash that doesn't collide
|
||||
for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){
|
||||
auto& bucket = buckets[current_bucket];
|
||||
//only handle buckets containing collisions
|
||||
if(bucket.size() <= 1)
|
||||
break;
|
||||
|
||||
const auto hashval = Hash{}(bucket[0].key, 0);
|
||||
|
||||
array<bool,N> pass_slots_used;
|
||||
vector<size_type,N> pass_slots;
|
||||
size_type d = 1;
|
||||
|
||||
for(size_type i = 0;i < bucket.size();){
|
||||
size_type slot = Hash{}(bucket[i].key, d) % max_size;
|
||||
if(pass_slots_used[slot] || m_key_hashes[slot] != 0){
|
||||
//slot already in use, try another value for 'd'
|
||||
++d;
|
||||
i = 0;
|
||||
pass_slots_used.fill(false);
|
||||
pass_slots.clear();
|
||||
}else{
|
||||
//slot is good to go
|
||||
pass_slots_used[slot] = true;
|
||||
pass_slots.push_back(slot);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
//store the successful value of 'd' at index of the first hash for this bucket
|
||||
m_g[hashval % max_size] = d;
|
||||
|
||||
//take the value from the temporary bucket into the permanent slot
|
||||
for(size_type i = 0;i < bucket.size();++i){
|
||||
m_values[pass_slots[i]] = std::move(bucket[i].value);
|
||||
m_key_hashes[pass_slots[i]] = hashval;
|
||||
}
|
||||
}
|
||||
|
||||
//Handle remaining single value buckets
|
||||
size_type next_free_slot = 0;
|
||||
|
||||
for(;current_bucket < buckets.size();++current_bucket){
|
||||
auto& bucket = buckets[current_bucket];
|
||||
if(bucket.size() == 0)
|
||||
break;
|
||||
|
||||
const auto hashval = Hash{}(bucket[0].key, 0);
|
||||
|
||||
for(;m_key_hashes[next_free_slot] != 0;++next_free_slot);
|
||||
|
||||
m_g[Hash{}(bucket[0].key, 0) % max_size] = (next_free_slot | single_bucket_bit);
|
||||
m_values[next_free_slot] = std::move(bucket[0].value);
|
||||
m_key_hashes[next_free_slot] = hashval;
|
||||
}
|
||||
}
|
||||
|
||||
//no key checks. give a correct key or get a random answer :)
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_values[d & ~single_bucket_bit];
|
||||
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
|
||||
}
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const noexcept -> const_reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_values[d & ~single_bucket_bit];
|
||||
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
|
||||
}
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr bool hashmap<Key,Value,N,Hash>::is_valid(U&& key)const noexcept{
|
||||
const auto hashval = UHash{}(std::forward<U>(key), 0);
|
||||
const auto d = m_g[hashval % max_size];
|
||||
if(d & single_bucket_bit){
|
||||
return m_key_hashes[d & ~single_bucket_bit] == hashval;
|
||||
}
|
||||
return m_key_hashes[UHash{}(std::forward<U>(key), d) % max_size] == hashval;
|
||||
}
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash = hash<Key>>
|
||||
constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){
|
||||
return hashmap<Key,Value,N,Hash>(list);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REXY_STRING_BASE_HPP
|
||||
#include "../string_hash.hpp"
|
||||
#endif
|
||||
#include "hashmap.tpp"
|
||||
|
||||
#endif
|
||||
|
||||
139
include/rexy/cx/hashmap.tpp
Normal file
139
include/rexy/cx/hashmap.tpp
Normal file
@ -0,0 +1,139 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_CX_HASHMAP_TPP
|
||||
#define REXY_CX_HASHMAP_TPP
|
||||
|
||||
#include <utility> //move, forward
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<class Key, class Value, std::size_t N, class Hash>
|
||||
constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N])
|
||||
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
|
||||
std::is_nothrow_copy_constructible<value_type>::value &&
|
||||
std::is_nothrow_move_assignable<mapped_type>::value &&
|
||||
std::is_nothrow_invocable<Hash,Key,std::size_t>::value)
|
||||
{
|
||||
array<vector<value_type,N>,N> buckets;
|
||||
array<size_type,N> key_hashes; //full hash values for keys to verify good index values
|
||||
size_type current_bucket = 0;
|
||||
|
||||
//place all keys into buckets
|
||||
for(auto& element : elements){
|
||||
buckets[Hash{}(element.key, 0) % max_size].push_back(element);
|
||||
}
|
||||
|
||||
//sort the buckets based on size, largest first
|
||||
quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{
|
||||
return left.size() > right.size();
|
||||
});
|
||||
|
||||
//for each bucket, try different values of 'd' to try to find a hash that doesn't collide
|
||||
for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){
|
||||
auto& bucket = buckets[current_bucket];
|
||||
//only handle buckets containing collisions
|
||||
if(bucket.size() <= 1)
|
||||
break;
|
||||
|
||||
const auto hashval = Hash{}(bucket[0].key, 0);
|
||||
|
||||
array<bool,N> pass_slots_used;
|
||||
vector<size_type,N> pass_slots;
|
||||
size_type d = 1;
|
||||
|
||||
for(size_type i = 0;i < bucket.size();){
|
||||
size_type slot = Hash{}(bucket[i].key, d) % max_size;
|
||||
if(pass_slots_used[slot] || key_hashes[slot] != 0){
|
||||
//slot already in use, try another value for 'd'
|
||||
++d;
|
||||
i = 0;
|
||||
pass_slots_used.fill(false);
|
||||
pass_slots.clear();
|
||||
}else{
|
||||
//slot is good to go
|
||||
pass_slots_used[slot] = true;
|
||||
pass_slots.push_back(slot);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
//store the successful value of 'd' at index of the first hash for this bucket
|
||||
m_g[hashval % max_size] = d;
|
||||
|
||||
//take the value from the temporary bucket into the permanent slot
|
||||
for(size_type i = 0;i < bucket.size();++i){
|
||||
m_elements[pass_slots[i]] = std::move(bucket[i]);
|
||||
key_hashes[pass_slots[i]] = hashval;
|
||||
}
|
||||
}
|
||||
|
||||
//Handle remaining single value buckets
|
||||
size_type next_free_slot = 0;
|
||||
|
||||
for(;current_bucket < buckets.size();++current_bucket){
|
||||
auto& bucket = buckets[current_bucket];
|
||||
if(bucket.size() == 0)
|
||||
break;
|
||||
|
||||
const auto hashval = Hash{}(bucket[0].key, 0);
|
||||
|
||||
for(;key_hashes[next_free_slot] != 0;++next_free_slot);
|
||||
|
||||
m_g[Hash{}(bucket[0].key, 0) % max_size] = (next_free_slot | single_bucket_bit);
|
||||
m_elements[next_free_slot] = std::move(bucket[0]);
|
||||
key_hashes[next_free_slot] = hashval;
|
||||
}
|
||||
}
|
||||
|
||||
//no key checks. give a correct key or get a random answer :)
|
||||
template<class Key, class Value, std::size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_elements[d & ~single_bucket_bit].value;
|
||||
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value;
|
||||
}
|
||||
template<class Key, class Value, std::size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const noexcept -> const_reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_elements[d & ~single_bucket_bit].value;
|
||||
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value;
|
||||
}
|
||||
|
||||
template<class Key, class Value, std::size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr bool hashmap<Key,Value,N,Hash>::contains(U&& key)const noexcept{
|
||||
const auto hashval = UHash{}(std::forward<U>(key), 0);
|
||||
const auto d = m_g[hashval % max_size];
|
||||
if(d & single_bucket_bit){
|
||||
return m_elements[d & ~single_bucket_bit].key == std::forward<U>(key);
|
||||
}
|
||||
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].key == std::forward<U>(key);
|
||||
}
|
||||
|
||||
template<class Key, class Value, std::size_t N, class Hash = hash<Key>>
|
||||
constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){
|
||||
return hashmap<Key,Value,N,Hash>(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -19,55 +19,69 @@
|
||||
#ifndef REXY_CX_STRING_HPP
|
||||
#define REXY_CX_STRING_HPP
|
||||
|
||||
#include <cstddef> //ptrdiff_t, size_t
|
||||
|
||||
namespace rexy::cx{
|
||||
template<size_t N, class Char>
|
||||
template<std::size_t N, class Char>
|
||||
class string;
|
||||
}
|
||||
|
||||
#include "../string_base.hpp"
|
||||
#include "../utility.hpp"
|
||||
#include <type_traits>
|
||||
#include <cstddef> //ptrdiff_t
|
||||
#include "../string_base.hpp" //string_cat_expr
|
||||
#include "../string_view.hpp"
|
||||
#include "../utility.hpp" //strlen, strcmp
|
||||
#include "../detail/string_appender.hpp"
|
||||
#include "../traits.hpp" //remove_cvref
|
||||
#include <type_traits> //nothrow_invocable, integral_constant, declval
|
||||
#include <iterator> //reverse_iterator
|
||||
|
||||
//This is different from rexy::static_string in that this doesn't hold a pointer to a constant string array.
|
||||
//This holds a mutable array of data which can be modified during compile time. static_string is
|
||||
#include "../compat/standard.hpp"
|
||||
|
||||
//This is different from rexy::string_view in that this doesn't hold a pointer to a constant string array.
|
||||
//This holds a mutable array of data which can be modified during compile time. string_view is
|
||||
//designed to be a thin wrapper around a raw char*, this is designed to allow compile time string concatenation
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<size_t N, class Char = char>
|
||||
template<std::size_t N, class Char = char>
|
||||
class string
|
||||
{
|
||||
public:
|
||||
using value_type = Char;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
public:
|
||||
static constexpr size_type max_size = N;
|
||||
static constexpr size_type npos = size_type(-1);
|
||||
|
||||
public:
|
||||
static constexpr size_t max_size = N;
|
||||
private:
|
||||
value_type m_data[N] = {};
|
||||
size_type m_length = 0;
|
||||
|
||||
public:
|
||||
constexpr string(void) = default;
|
||||
template<size_type M>
|
||||
constexpr string(const char(&data)[M])noexcept:
|
||||
constexpr string(const value_type(&data)[M])noexcept:
|
||||
m_length(M)
|
||||
{
|
||||
static_assert(M <= N);
|
||||
for(size_type i = 0;i < M;++i){
|
||||
m_data[i] = data[i];
|
||||
}
|
||||
if(m_data[m_length - 1] == 0){
|
||||
--m_length; //exclude null terminator in length
|
||||
}
|
||||
}
|
||||
constexpr string(const_pointer data)noexcept:
|
||||
m_length(strlen(data))
|
||||
m_length(rexy::strlen(data))
|
||||
{
|
||||
for(size_type i = 0;i < m_length;++i){
|
||||
m_data[i] = data[i];
|
||||
@ -81,7 +95,7 @@ namespace rexy::cx{
|
||||
}
|
||||
}
|
||||
|
||||
constexpr string(const rexy::static_string<value_type>& str)noexcept:
|
||||
constexpr string(const rexy::basic_string_view<value_type>& str)noexcept:
|
||||
m_length(str.length())
|
||||
{
|
||||
for(size_type i = 0;i < m_length;++i){
|
||||
@ -90,7 +104,7 @@ namespace rexy::cx{
|
||||
}
|
||||
template<class Left, class Right>
|
||||
constexpr string(const rexy::string_cat_expr<Left,Right>& expr)
|
||||
noexcept(std::is_nothrow_invocable<rexy::detail::string_appender<cx::string<N>>, decltype(expr)>::value)
|
||||
noexcept(std::is_nothrow_invocable<rexy::detail::string_appender<cx::string<N,Char>>, decltype(expr)>::value)
|
||||
{
|
||||
rexy::detail::string_appender<cx::string<N,value_type>> append(*this);
|
||||
append(expr);
|
||||
@ -98,10 +112,10 @@ namespace rexy::cx{
|
||||
|
||||
constexpr string(const string&)noexcept = default;
|
||||
constexpr string(string&&)noexcept = default;
|
||||
~string(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~string(void)noexcept = default;
|
||||
|
||||
constexpr string& operator=(const_pointer c)noexcept{
|
||||
m_length = strlen(c);
|
||||
m_length = rexy::strlen(c);
|
||||
for(size_type i = 0;i < m_length;++i){
|
||||
m_data[i] = c[i];
|
||||
}
|
||||
@ -110,45 +124,64 @@ namespace rexy::cx{
|
||||
constexpr string& operator=(const string&)noexcept = default;
|
||||
constexpr string& operator=(string&&)noexcept = default;
|
||||
|
||||
constexpr bool operator==(const string& s)noexcept{return !rexy::strcmp(m_data, s.m_data);}
|
||||
constexpr bool operator!=(const string& s)noexcept{return rexy::strcmp(m_data, s.m_data);}
|
||||
|
||||
constexpr size_type length(void)const noexcept{
|
||||
return m_length;
|
||||
constexpr size_type length(void)const noexcept{return m_length;}
|
||||
constexpr size_type size(void)const noexcept{return m_length;}
|
||||
constexpr size_type capacity(void)const noexcept{return max_size;}
|
||||
constexpr bool empty(void)const noexcept{return m_length == 0;}
|
||||
|
||||
constexpr pointer c_str(void)noexcept{return m_data;}
|
||||
constexpr const_pointer c_str(void)const noexcept{return m_data;}
|
||||
constexpr pointer get(void)noexcept{return m_data;}
|
||||
constexpr const_pointer get(void)const noexcept{return m_data;}
|
||||
constexpr operator pointer(void)noexcept{return m_data;}
|
||||
constexpr operator const_pointer(void)const noexcept{return m_data;}
|
||||
|
||||
constexpr reference operator[](size_type i)noexcept{return m_data[i];}
|
||||
constexpr const_reference operator[](size_type i)const noexcept{return m_data[i];}
|
||||
constexpr reference at(size_type i)noexcept{return m_data[i];}
|
||||
constexpr const_reference at(size_type i)const noexcept{return m_data[i];}
|
||||
constexpr const_reference front(size_type i)const noexcept{return m_data[0];}
|
||||
constexpr const_reference back(size_type i)const noexcept{return m_data[m_length-1];}
|
||||
|
||||
constexpr const_iterator search(const basic_string_view<value_type>& s)const{
|
||||
return two_way_search(cbegin(), cend(), s.cbegin(), s.cend());
|
||||
}
|
||||
constexpr size_type capacity(void)const noexcept{
|
||||
return max_size;
|
||||
constexpr const_iterator search(const_pointer c)const{
|
||||
return search(basic_string_view<value_type>{c});
|
||||
}
|
||||
constexpr pointer c_str(void)noexcept{
|
||||
return m_data;
|
||||
template<class Searcher>
|
||||
constexpr const_iterator search(const basic_string_view<value_type>& s, Searcher&& searcher)const{
|
||||
return searcher(cbegin(), cend(), s.cbegin(), s.cend());
|
||||
}
|
||||
constexpr const_pointer c_str(void)const noexcept{
|
||||
return m_data;
|
||||
}
|
||||
constexpr pointer get(void)noexcept{
|
||||
return m_data;
|
||||
}
|
||||
constexpr const_pointer get(void)const noexcept{
|
||||
return m_data;
|
||||
}
|
||||
constexpr operator pointer(void)noexcept{
|
||||
return m_data;
|
||||
}
|
||||
constexpr operator const_pointer(void)const noexcept{
|
||||
return m_data;
|
||||
template<class Searcher>
|
||||
constexpr const_iterator search(const_pointer c, Searcher&& searcher)const{
|
||||
return search(basic_string_view<value_type>{c}, searcher);
|
||||
}
|
||||
|
||||
constexpr bool valid(void)const noexcept{
|
||||
return m_length > 0;
|
||||
}
|
||||
constexpr reference operator[](size_type i)noexcept{
|
||||
return m_data[i];
|
||||
}
|
||||
constexpr const_reference operator[](size_type i)const noexcept{
|
||||
return m_data[i];
|
||||
}
|
||||
constexpr iterator begin(void)noexcept{return m_data;}
|
||||
constexpr const_iterator begin(void)const noexcept{return m_data;}
|
||||
constexpr const_iterator cbegin(void)const noexcept{return m_data;}
|
||||
constexpr iterator end(void)noexcept{return m_data+m_length;}
|
||||
constexpr const_iterator end(void)const noexcept{return m_data+m_length;}
|
||||
constexpr const_iterator cend(void)const noexcept{return m_data+m_length;}
|
||||
|
||||
constexpr const_reverse_iterator rbegin(void)const{return const_reverse_iterator(m_data+m_length);}
|
||||
constexpr const_reverse_iterator rend(void)const{return const_reverse_iterator(m_data-1);}
|
||||
constexpr const_reverse_iterator crbegin(void)const{return rbegin();}
|
||||
constexpr const_reverse_iterator crend(void)const{return rend();}
|
||||
|
||||
constexpr bool valid(void)const noexcept{return m_length > 0;}
|
||||
|
||||
constexpr bool compare(const string& s)const{return *this == s;}
|
||||
constexpr bool compare(const_pointer c)const{return *this == c;}
|
||||
|
||||
constexpr bool resize(size_type i)noexcept{
|
||||
if(i >= capacity())
|
||||
if(i >= capacity()){
|
||||
return false;
|
||||
}
|
||||
m_length = i;
|
||||
m_data[m_length] = 0;
|
||||
return true;
|
||||
@ -159,48 +192,48 @@ namespace rexy::cx{
|
||||
m_data[m_length++] = data[i];
|
||||
}
|
||||
}
|
||||
constexpr void append(const_pointer data)noexcept{
|
||||
append(data, strlen(data));
|
||||
}
|
||||
constexpr void append(const string& s)noexcept{
|
||||
append(s.get(), s.length());
|
||||
}
|
||||
constexpr void append(const rexy::static_string<value_type>& s)noexcept{
|
||||
append(s.get(), s.length());
|
||||
}
|
||||
};
|
||||
constexpr void append(const_pointer data)noexcept{append(data, rexy::strlen(data));}
|
||||
constexpr void append(const string& s)noexcept{append(s.get(), s.length());}
|
||||
constexpr void append(const rexy::basic_string_view<value_type>& s)noexcept{append(s.get(), s.length());}
|
||||
|
||||
constexpr size_type find_first_of(value_type v, size_type start = 0)const{
|
||||
return rexy::find_first_of(*this, &v, start, 1);
|
||||
}
|
||||
constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const{
|
||||
return rexy::find_first_of(*this, c, pos, rexy::strlen(c));
|
||||
}
|
||||
constexpr size_type find_first_of(const_pointer c, size_type start, size_type size)const{
|
||||
return rexy::find_first_of(*this, c, start, size);
|
||||
}
|
||||
constexpr size_type find_last_of(value_type v, size_type start = 0)const{
|
||||
return rexy::find_last_of(*this, &v, start, 1);
|
||||
}
|
||||
constexpr size_type find_last_of(const_pointer c, size_type start = 0)const{
|
||||
return rexy::find_last_of(*this, c, start, rexy::strlen(c));
|
||||
}
|
||||
constexpr size_type find_last_of(const_pointer c, size_type start, size_type size)const{
|
||||
return rexy::find_last_of(*this, c, start, size);
|
||||
}
|
||||
|
||||
namespace detail{
|
||||
template<class T>
|
||||
struct is_cx_string_helper{
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<size_t N, class Char>
|
||||
struct is_cx_string_helper<cx::string<N,Char>>{
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<class Char, std::size_t N>
|
||||
string(const Char(&data)[N]) -> string<N, Char>;
|
||||
|
||||
template<class... Ts>
|
||||
struct is_cx_string{
|
||||
static constexpr bool value = (is_cx_string_helper<std::decay_t<Ts>>::value && ...);
|
||||
template<class Char, std::size_t N>
|
||||
static std::true_type check(cx::string<N,Char>);
|
||||
static std::false_type check(...);
|
||||
static constexpr bool value = (decltype(check(std::declval<rexy::remove_cvref_t<Ts>>()))::value && ...);
|
||||
};
|
||||
}
|
||||
|
||||
template<class Str1, class Str2, std::enable_if_t<detail::is_cx_string<Str1,Str2>::value,int> = 0>
|
||||
constexpr auto operator+(Str1&& l, Str2&& r)noexcept{
|
||||
return string_cat_expr(std::forward<Str1>(l), std::forward<Str2>(r));
|
||||
}
|
||||
template<class Str1, std::enable_if_t<detail::is_cx_string<Str1>::value,int> = 0>
|
||||
constexpr auto operator+(Str1&& l, const char* r)noexcept{
|
||||
return string_cat_expr(std::forward<Str1>(l), rexy::static_string<char>(r));
|
||||
}
|
||||
template<class Str1, std::enable_if_t<detail::is_cx_string<Str1>::value,int> = 0>
|
||||
constexpr auto operator+(const char* l, Str1&& r)noexcept{
|
||||
return string_cat_expr(rexy::static_string<char>(l), std::forward<Str1>(r));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REXY_CX_HASH_HPP
|
||||
#include "cx_string_hash.hpp"
|
||||
#endif
|
||||
#ifdef __cpp_concepts
|
||||
#include "../compat/cpp20/cx/string.hpp"
|
||||
#else //__cpp_concepts
|
||||
#include "../compat/cpp17/cx/string.hpp"
|
||||
#endif //__cpp_concepts
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@ -24,17 +24,18 @@
|
||||
|
||||
#include "detail/bool_specialize_base.hpp"
|
||||
#include "../utility.hpp" //swap
|
||||
#include "../compat/standard.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<class T, size_t N>
|
||||
template<class T, std::size_t N>
|
||||
class vector
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
@ -63,7 +64,7 @@ namespace rexy::cx{
|
||||
m_size = min(count, max_elements);
|
||||
}
|
||||
|
||||
~vector(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
|
||||
|
||||
constexpr vector& operator=(const vector&)noexcept(std::is_nothrow_copy_assignable<T>::value) = default;
|
||||
constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable<T>::value) = default;
|
||||
@ -243,7 +244,7 @@ namespace rexy::cx{
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
template<std::size_t N>
|
||||
class vector<bool,N> : public detail::bool_specialize_base
|
||||
{
|
||||
public:
|
||||
@ -283,7 +284,7 @@ namespace rexy::cx{
|
||||
m_size = min(count, max_elements);
|
||||
}
|
||||
|
||||
~vector(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
|
||||
|
||||
constexpr vector& operator=(const vector&)noexcept = default;
|
||||
constexpr vector& operator=(vector&&)noexcept = default;
|
||||
|
||||
212
include/rexy/debug_print.hpp
Normal file
212
include/rexy/debug_print.hpp
Normal file
@ -0,0 +1,212 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DEBUG_PRINT_HPP
|
||||
#define REXY_DEBUG_PRINT_HPP
|
||||
|
||||
#include "detail/debug_config.hpp"
|
||||
|
||||
//Debug output section
|
||||
#if LIBREXY_ENABLE_DEBUG_LEVEL > 0
|
||||
#define LIBREXY_ENABLE_DEBUG_OUTPUT
|
||||
#endif
|
||||
#if LIBREXY_ENABLE_DEBUG_LEVEL > 2
|
||||
#define LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LIBREXY_ENABLE_DEBUG_OUTPUT
|
||||
#include <cstdio> //fprintf, vfprintf
|
||||
#include <utility> //forward
|
||||
|
||||
#include "compat/source_location.hpp"
|
||||
|
||||
|
||||
namespace rexy::debug{
|
||||
namespace detail{
|
||||
static constexpr const char color_red[] = "\033[38;5;9m";
|
||||
static constexpr const char color_yellow[] = "\033[38;5;11m";
|
||||
static constexpr const char color_green[] = "\033[38;5;2m";
|
||||
static constexpr const char color_blue[] = "\033[38;5;12m";
|
||||
static constexpr const char color_clear[] = "\033[0m";
|
||||
}
|
||||
template<class... Args>
|
||||
struct print{
|
||||
explicit print(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){
|
||||
std::fprintf(stderr, "%s:%s:%d: ", loc.file_name(), loc.function_name(), loc.line());
|
||||
std::fprintf(stderr, std::forward<Args>(args)...);
|
||||
}
|
||||
print(const print&) = delete;
|
||||
print(print&&) = delete;
|
||||
print& operator=(const print&) = delete;
|
||||
print& operator=(print&&) = delete;
|
||||
};
|
||||
template<class... Args>
|
||||
print(Args&&...) -> print<Args&&...>;
|
||||
#ifdef LIBREXY_ENABLE_COLOR_DEBUG
|
||||
template<class... Args>
|
||||
struct print_succ{
|
||||
explicit print_succ(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){
|
||||
std::fprintf(stderr, "%s", detail::color_green);
|
||||
std::fprintf(stderr, "%s:%s:%d: ", loc.file_name(), loc.function_name(), loc.line());
|
||||
std::fprintf(stderr, std::forward<Args>(args)...);
|
||||
std::fprintf(stderr, "%s", detail::color_clear);
|
||||
}
|
||||
print_succ(const print_succ&) = delete;
|
||||
print_succ(print_succ&&) = delete;
|
||||
print_succ& operator=(const print_succ&) = delete;
|
||||
print_succ& operator=(print_succ&&) = delete;
|
||||
};
|
||||
template<class... Args>
|
||||
struct print_info{
|
||||
explicit print_info(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){
|
||||
std::fprintf(stderr, "%s", detail::color_blue);
|
||||
std::fprintf(stderr, "%s:%s:%d: ", loc.file_name(), loc.function_name(), loc.line());
|
||||
std::fprintf(stderr, std::forward<Args>(args)...);
|
||||
std::fprintf(stderr, "%s", detail::color_clear);
|
||||
}
|
||||
print_info(const print_info&) = delete;
|
||||
print_info(print_info&&) = delete;
|
||||
print_info& operator=(const print_info&) = delete;
|
||||
print_info& operator=(print_info&&) = delete;
|
||||
};
|
||||
template<class... Args>
|
||||
struct print_warn{
|
||||
explicit print_warn(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){
|
||||
std::fprintf(stderr, "%s", detail::color_yellow);
|
||||
std::fprintf(stderr, "%s:%s:%d: ", loc.file_name(), loc.function_name(), loc.line());
|
||||
std::fprintf(stderr, std::forward<Args>(args)...);
|
||||
std::fprintf(stderr, "%s", detail::color_clear);
|
||||
}
|
||||
print_warn(const print_warn&) = delete;
|
||||
print_warn(print_warn&&) = delete;
|
||||
print_warn& operator=(const print_warn&) = delete;
|
||||
print_warn& operator=(print_warn&&) = delete;
|
||||
};
|
||||
template<class... Args>
|
||||
struct print_error{
|
||||
explicit print_error(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){
|
||||
std::fprintf(stderr, "%s", detail::color_red);
|
||||
std::fprintf(stderr, "%s:%s:%d: ", loc.file_name(), loc.function_name(), loc.line());
|
||||
std::fprintf(stderr, std::forward<Args>(args)...);
|
||||
std::fprintf(stderr, "%s", detail::color_clear);
|
||||
}
|
||||
print_error(const print_error&) = delete;
|
||||
print_error(print_error&&) = delete;
|
||||
print_error& operator=(const print_error&) = delete;
|
||||
print_error& operator=(print_error&&) = delete;
|
||||
};
|
||||
#else
|
||||
template<class... Args>
|
||||
class print_succ : public print<Args...>{
|
||||
public:
|
||||
using print<Args...>::print;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_info : public print<Args...>{
|
||||
public:
|
||||
using print<Args...>::print;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_warn : public print<Args...>{
|
||||
public:
|
||||
using print<Args...>::print;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_error : public print<Args...>{
|
||||
public:
|
||||
using print<Args...>::print;
|
||||
};
|
||||
#endif //LIBREXY_ENABLE_COLOR_DEBUG
|
||||
template<class... Args>
|
||||
print_warn(Args&&...) -> print_warn<Args&&...>;
|
||||
template<class... Args>
|
||||
print_error(Args&&...) -> print_error<Args&&...>;
|
||||
template<class... Args>
|
||||
print_info(Args&&...) -> print_info<Args&&...>;
|
||||
template<class... Args>
|
||||
print_succ(Args&&...) -> print_succ<Args&&...>;
|
||||
|
||||
#ifdef LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
||||
namespace verbose{
|
||||
template<class... Args>
|
||||
class print : public ::rexy::debug::print<Args...>{
|
||||
public:
|
||||
using ::rexy::debug::print<Args...>::print;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_succ : public ::rexy::debug::print_succ<Args...>{
|
||||
public:
|
||||
using ::rexy::debug::print_succ<Args...>::print_succ;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_info : public ::rexy::debug::print_info<Args...>{
|
||||
public:
|
||||
using ::rexy::debug::print_info<Args...>::print_info;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_warn : public ::rexy::debug::print_warn<Args...>{
|
||||
public:
|
||||
using ::rexy::debug::print_warn<Args...>::print_warn;
|
||||
};
|
||||
template<class... Args>
|
||||
class print_error : public ::rexy::debug::print_error<Args...>{
|
||||
public:
|
||||
using ::rexy::debug::print_error<Args...>::print_error;
|
||||
};
|
||||
template<class... Args>
|
||||
print(Args&&...) -> print<Args&&...>;
|
||||
template<class... Args>
|
||||
print_warn(Args&&...) -> print_warn<Args&&...>;
|
||||
template<class... Args>
|
||||
print_error(Args&&...) -> print_error<Args&&...>;
|
||||
template<class... Args>
|
||||
print_info(Args&&...) -> print_info<Args&&...>;
|
||||
template<class... Args>
|
||||
print_succ(Args&&...) -> print_succ<Args&&...>;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
namespace verbose{
|
||||
static constexpr inline void print(...){}
|
||||
static constexpr inline void print_succ(...){}
|
||||
static constexpr inline void print_info(...){}
|
||||
static constexpr inline void print_warn(...){}
|
||||
static constexpr inline void print_error(...){}
|
||||
}
|
||||
#endif //LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
||||
}
|
||||
#else
|
||||
namespace rexy::debug{
|
||||
static constexpr inline void print(...){}
|
||||
static constexpr inline void print_succ(...){}
|
||||
static constexpr inline void print_info(...){}
|
||||
static constexpr inline void print_warn(...){}
|
||||
static constexpr inline void print_error(...){}
|
||||
namespace verbose{
|
||||
static constexpr inline void print(...){}
|
||||
static constexpr inline void print_succ(...){}
|
||||
static constexpr inline void print_info(...){}
|
||||
static constexpr inline void print_warn(...){}
|
||||
static constexpr inline void print_error(...){}
|
||||
}
|
||||
}
|
||||
#endif //LIBREXY_ENABLE_DEBUG_OUTPUT
|
||||
|
||||
#endif
|
||||
134
include/rexy/deferred.hpp
Normal file
134
include/rexy/deferred.hpp
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_UTIL_DEFERRED_HPP
|
||||
#define REXY_UTIL_DEFERRED_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <utility> //forward, index_sequence
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
namespace rexy{
|
||||
template<class T, class... Args>
|
||||
class deferred
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
private:
|
||||
std::tuple<Args&&...> m_args;
|
||||
|
||||
public:
|
||||
template<class... FArgs>
|
||||
constexpr deferred(FArgs&&... args);
|
||||
|
||||
REXY_CPP20_CONSTEXPR deferred(const deferred&) = default;
|
||||
REXY_CPP20_CONSTEXPR deferred(deferred&&) = default;
|
||||
REXY_CPP20_CONSTEXPR ~deferred(void) = default;
|
||||
REXY_CPP20_CONSTEXPR deferred& operator=(const deferred&) = default;
|
||||
REXY_CPP20_CONSTEXPR deferred& operator=(deferred&&) = default;
|
||||
|
||||
constexpr value_type value(void);
|
||||
constexpr operator value_type(void);
|
||||
|
||||
private:
|
||||
template<std::size_t... Is>
|
||||
constexpr value_type get_value_(std::index_sequence<Is...>);
|
||||
};
|
||||
|
||||
template<class Fn, class... Args>
|
||||
class deferred_function
|
||||
{
|
||||
public:
|
||||
using ret_t = decltype(std::declval<Fn>()(std::declval<Args>()...));
|
||||
|
||||
private:
|
||||
Fn m_fn;
|
||||
std::tuple<Args&&...> m_args;
|
||||
|
||||
public:
|
||||
template<class F, class... FArgs>
|
||||
constexpr deferred_function(F&& f, FArgs&&... args);
|
||||
|
||||
constexpr decltype(auto) operator()(void);
|
||||
|
||||
constexpr decltype(auto) value(void);
|
||||
constexpr operator ret_t(void);
|
||||
|
||||
private:
|
||||
template<std::size_t... Is>
|
||||
constexpr decltype(auto) get_value_(std::index_sequence<Is...>);
|
||||
};
|
||||
|
||||
template<class T, class... Args>
|
||||
template<class... FArgs>
|
||||
constexpr deferred<T,Args...>::deferred(FArgs&&... args):
|
||||
m_args{std::forward<FArgs>(args)...}{}
|
||||
template<class T, class... Args>
|
||||
constexpr auto deferred<T,Args...>::value(void) -> value_type{
|
||||
return get_value_(std::make_index_sequence<sizeof...(Args)>());
|
||||
}
|
||||
template<class T, class... Args>
|
||||
constexpr deferred<T,Args...>::operator value_type(void){
|
||||
return get_value_(std::make_index_sequence<sizeof...(Args)>());
|
||||
}
|
||||
template<class T, class... Args>
|
||||
template<std::size_t... Is>
|
||||
constexpr auto deferred<T,Args...>::get_value_(std::index_sequence<Is...>) -> value_type{
|
||||
return T{std::forward<std::tuple_element_t<Is,decltype(m_args)>>(std::get<Is>(m_args))...};
|
||||
}
|
||||
|
||||
template<class Fn, class... Args>
|
||||
template<class F, class... FArgs>
|
||||
constexpr deferred_function<Fn,Args...>::deferred_function(F&& f, FArgs&&... args):
|
||||
m_fn(std::forward<F>(f)),
|
||||
m_args{std::forward<FArgs>(args)...}{}
|
||||
|
||||
template<class Fn, class... Args>
|
||||
constexpr decltype(auto) deferred_function<Fn,Args...>::operator()(void){
|
||||
return get_value_(std::make_index_sequence<sizeof...(Args)>());
|
||||
}
|
||||
|
||||
template<class Fn, class... Args>
|
||||
constexpr decltype(auto) deferred_function<Fn,Args...>::value(void){
|
||||
return get_value_(std::make_index_sequence<sizeof...(Args)>());
|
||||
}
|
||||
template<class Fn, class... Args>
|
||||
constexpr deferred_function<Fn,Args...>::operator ret_t(void){
|
||||
return get_value_(std::make_index_sequence<sizeof...(Args)>());
|
||||
}
|
||||
template<class Fn, class... Args>
|
||||
template<std::size_t... Is>
|
||||
constexpr decltype(auto) deferred_function<Fn,Args...>::get_value_(std::index_sequence<Is...>){
|
||||
return std::forward<Fn>(m_fn)(std::forward<std::tuple_element_t<Is,decltype(m_args)>>(std::get<Is>(m_args))...);
|
||||
}
|
||||
|
||||
template<class Fn, class... Args>
|
||||
deferred_function(Fn&&, Args&&...) -> deferred_function<Fn&&, Args&&...>;
|
||||
|
||||
|
||||
template<class T, class... Args>
|
||||
deferred<T,Args...> make_deferred(Args&&... args){
|
||||
return deferred<T,Args...>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,11 +16,14 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rexy/binary.hpp"
|
||||
#include "rexy/allocator.hpp"
|
||||
#ifndef REXY_UTIL_DEMANGLE_HPP
|
||||
#define REXY_UTIL_DEMANGLE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template class basic_binary<allocator<char>>;
|
||||
|
||||
std::string demangle(const char* name);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -22,41 +22,40 @@
|
||||
#include "../utility.hpp" //swap
|
||||
|
||||
#include <type_traits> //nothrow_invocable
|
||||
#include <functional> //less, greater
|
||||
#include <utility> //pair
|
||||
#include <utility> //pair, forward
|
||||
#include <iterator> //iterator_traits
|
||||
#include <cstdlib> //size_t
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy::detail{
|
||||
|
||||
template<class Iter, class Compare>
|
||||
constexpr Iter qs_partition(Iter left, Iter right, const Compare& cmp)
|
||||
noexcept(std::is_nothrow_invocable<Compare,decltype(*left),decltype(*right)>::value &&
|
||||
noexcept(swap(*left,*right)))
|
||||
noexcept(::rexy::swap(*left,*right)))
|
||||
{
|
||||
auto range = right - left;
|
||||
auto pivot = left + (range / 2);
|
||||
auto value = *pivot;
|
||||
|
||||
//move pivot value all the way to the right side to preserve it
|
||||
swap(*pivot, *right);
|
||||
::rexy::swap(*pivot, *right);
|
||||
for(auto it = left;it != right;++it){
|
||||
if(cmp(*it, value)){
|
||||
swap(*left, *it);
|
||||
::rexy::swap(*left, *it);
|
||||
++left;
|
||||
}
|
||||
}
|
||||
//move pivot value back to proper position
|
||||
swap(*left, *right);
|
||||
::rexy::swap(*left, *right);
|
||||
return left;
|
||||
}
|
||||
template<class Iter, class Op>
|
||||
constexpr std::pair<size_t,size_t> max_suffix(const Iter& needle, size_t nlen, const Op& op = Op()){
|
||||
constexpr std::pair<std::size_t,size_t> max_suffix(const Iter& needle, std::size_t nlen, const Op& op = Op()){
|
||||
using value_type = typename std::iterator_traits<Iter>::value_type;
|
||||
size_t max_suffix = -1;
|
||||
size_t j = 0;
|
||||
size_t k = 1;
|
||||
size_t period = 1;
|
||||
std::size_t max_suffix = -1;
|
||||
std::size_t j = 0;
|
||||
std::size_t k = 1;
|
||||
std::size_t period = 1;
|
||||
value_type a;
|
||||
value_type b;
|
||||
|
||||
@ -81,18 +80,23 @@ namespace rexy::detail{
|
||||
}
|
||||
return {max_suffix, period};
|
||||
}
|
||||
|
||||
template<class Iter>
|
||||
constexpr std::pair<size_t,size_t> critical_factorization(const Iter& nstart, const Iter& nend){
|
||||
auto msuffix = max_suffix(nstart, nend - nstart, std::less{});
|
||||
auto msuffix_rev = max_suffix(nstart, nend - nstart, std::greater{});
|
||||
constexpr std::pair<std::size_t,size_t> critical_factorization(const Iter& nstart, const Iter& nend){
|
||||
auto msuffix = max_suffix(nstart, nend - nstart, []<class T, class U>(T&& left, U&& right) constexpr{
|
||||
return std::forward<T>(left) < std::forward<U>(right);
|
||||
});
|
||||
auto msuffix_rev = max_suffix(nstart, nend - nstart, []<class T, class U>(T&& left, U&& right) constexpr{
|
||||
return std::forward<T>(left) > std::forward<U>(right);
|
||||
});
|
||||
if(msuffix.first < msuffix_rev.first){
|
||||
return msuffix_rev;
|
||||
}
|
||||
return msuffix;
|
||||
}
|
||||
template<class Iter>
|
||||
constexpr bool iter_compare(const Iter& left, const Iter& right, size_t length){
|
||||
for(size_t i = 0;i < length;++i){
|
||||
constexpr bool iter_compare(const Iter& left, const Iter& right, std::size_t length){
|
||||
for(std::size_t i = 0;i < length;++i){
|
||||
if(left[i] != right[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BINARY_STRING_CONV_HPP
|
||||
#define REXY_BINARY_STRING_CONV_HPP
|
||||
|
||||
#include "rexy/string_base.hpp"
|
||||
#include "rexy/binary_base.hpp"
|
||||
#include <cstring> //memcpy
|
||||
|
||||
namespace rexy{
|
||||
template<class Str, detail::enable_if_concrete_string<Str> = 0>
|
||||
auto binary_to_string(const binary_base& b)
|
||||
noexcept(std::is_nothrow_constructible<Str, decltype(b.size())>::value)
|
||||
{
|
||||
Str s(b.size()+1);
|
||||
s.append(b.get(), b.size());
|
||||
return s;
|
||||
}
|
||||
template<class Al, class Str, detail::enable_if_concrete_string<Str> = 0, std::enable_if_t<std::is_same<std::decay_t<Al>,typename Str::allocator_type>::value,int> = 0>
|
||||
auto binary_to_string(basic_binary<Al>&& b)noexcept{
|
||||
return Str(rexy::steal(b.release()), b.size());
|
||||
}
|
||||
template<class Bin, detail::enable_if_binary<Bin> = 0>
|
||||
auto string_to_binary(const string_base<char>& s)
|
||||
noexcept(std::is_nothrow_constructible<Bin, decltype(s.length())>::value &&
|
||||
noexcept(std::decay_t<Bin>::allocator_type::allocate(0)))
|
||||
{
|
||||
Bin b(s.length()+1);
|
||||
b.append(s.get(), s.length()+1);
|
||||
return b;
|
||||
}
|
||||
template<class Al, class Bin, detail::enable_if_binary<Bin> = 0, std::enable_if_t<std::is_same<std::decay_t<Al>,typename Bin::allocator_type>::value,int> = 0>
|
||||
auto string_to_binary(basic_string<char,Al>&& s)noexcept{
|
||||
return Bin(rexy::steal(s.release()), s.length());
|
||||
}
|
||||
|
||||
} //namespace rexy
|
||||
|
||||
#endif
|
||||
26
include/rexy/detail/debug_config.hpp
Normal file
26
include/rexy/detail/debug_config.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIBREXY_ENABLE_DEBUG_LEVEL
|
||||
#define LIBREXY_ENABLE_DEBUG_LEVEL 0
|
||||
#endif
|
||||
|
||||
#ifndef LIBREXY_ENABLE_COLOR_DEBUG
|
||||
#define LIBREXY_ENABLE_COLOR_DEBUG 1
|
||||
#endif
|
||||
105
include/rexy/detail/format/arg_store.hpp
Normal file
105
include/rexy/detail/format/arg_store.hpp
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_ARG_STORE_HPP
|
||||
#define REXY_DETAIL_FORMAT_ARG_STORE_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include "basic_types.hpp" //arg_info
|
||||
#include "storage.hpp" //stored_type
|
||||
#include "named_args.hpp" //count_named_args
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
//Implementation of argument storage, erasing the need of passing around a parameter pack. Used in
|
||||
//constructor of basic_format_args
|
||||
template<class Context, class... Args>
|
||||
class basic_format_arg_store
|
||||
{
|
||||
public:
|
||||
using char_type = typename Context::char_type;
|
||||
public:
|
||||
static constexpr std::size_t num_args = sizeof...(Args);
|
||||
static constexpr std::size_t num_named_args = count_named_args_v<Args...>;
|
||||
|
||||
struct format_data
|
||||
{
|
||||
friend class basic_format_arg_store;
|
||||
private:
|
||||
unsigned char m_data[
|
||||
(sizeof(arg_info) * num_args) +
|
||||
(sizeof(basic_string_view<char_type>) * num_named_args) +
|
||||
(sizeof(stored_type_t<Args,Context>) + ...)
|
||||
] = {};
|
||||
|
||||
arg_info* info(void){
|
||||
return reinterpret_cast<arg_info*>(raw_info());
|
||||
}
|
||||
constexpr unsigned char* data(void){
|
||||
return raw_info() + (sizeof(arg_info) * num_args);
|
||||
}
|
||||
|
||||
constexpr unsigned char* raw_info(void){
|
||||
return m_data;
|
||||
}
|
||||
constexpr const unsigned char* raw_info(void)const{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
public:
|
||||
const arg_info* info(void)const{
|
||||
return reinterpret_cast<const arg_info*>(raw_info());
|
||||
}
|
||||
const unsigned char* data(void)const{
|
||||
return raw_info() + (sizeof(arg_info) * num_args);
|
||||
}
|
||||
|
||||
const unsigned char* raw(void)const{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
}packed_data;
|
||||
|
||||
basic_format_arg_store(Args&&... args);
|
||||
|
||||
private:
|
||||
template<class Arg>
|
||||
void store_arg(std::size_t argnum, Arg&& arg);
|
||||
template<NamedArg Arg>
|
||||
void store_arg(std::size_t argnum, Arg&& arg);
|
||||
|
||||
template<class... SArgs>
|
||||
void store_args(SArgs&&... args);
|
||||
};
|
||||
|
||||
//Specialized for empty parameter pack
|
||||
template<class Context>
|
||||
class basic_format_arg_store<Context>
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t num_args = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
89
include/rexy/detail/format/arg_store.tpp
Normal file
89
include/rexy/detail/format/arg_store.tpp
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_ARG_STORE_TPP
|
||||
#define REXY_DETAIL_FORMAT_ARG_STORE_TPP
|
||||
|
||||
#include "arg_store.hpp"
|
||||
|
||||
#include "storage.hpp"
|
||||
#include "basic_types.hpp"
|
||||
|
||||
#include "../../utility.hpp" //memcpy
|
||||
|
||||
#include <utility> //forward
|
||||
#include <memory> //addressof
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
///////////////////////basic_format_arg_store////////////////////////
|
||||
template<class Context, class... Args>
|
||||
basic_format_arg_store<Context,Args...>::basic_format_arg_store(Args&&... args){
|
||||
store_args(std::forward<Args>(args)...);
|
||||
}
|
||||
template<class Context, class... Args>
|
||||
template<class Arg>
|
||||
void basic_format_arg_store<Context,Args...>::store_arg(std::size_t argnum, Arg&& arg){
|
||||
using stored_type = stored_type_t<Arg,Context>;
|
||||
arg_info& ai = packed_data.info()[argnum];
|
||||
ai.type = map_to_storage_enum_v<stored_type,typename Context::char_type>;
|
||||
|
||||
//convert to one of the storable types
|
||||
stored_type st{std::forward<Arg>(arg)};
|
||||
|
||||
//save to the array of data
|
||||
rexy::memcpy(packed_data.data()+ai.offset, std::addressof(st), sizeof(st));
|
||||
|
||||
//setup next entry's offset
|
||||
if(argnum+1 < num_args){
|
||||
packed_data.info()[argnum+1].offset = ai.offset + sizeof(st);
|
||||
}
|
||||
}
|
||||
template<class Context, class... Args>
|
||||
template<NamedArg Arg>
|
||||
void basic_format_arg_store<Context,Args...>::store_arg(std::size_t argnum, Arg&& arg){
|
||||
using stored_type = stored_type_t<Arg,Context>;
|
||||
arg_info& ai = packed_data.info()[argnum];
|
||||
ai.type = map_to_storage_enum_v<stored_type,typename Context::char_type>;
|
||||
ai.named = true;
|
||||
const std::size_t name_size = sizeof(format_string_view<char_type>);
|
||||
|
||||
//convert to one of the storable types
|
||||
stored_type st{std::forward<Arg>(arg).value};
|
||||
format_string_view<char_type> name{arg.name.c_str(), arg.name.length()};
|
||||
|
||||
//save to the array of data
|
||||
rexy::memcpy(packed_data.data()+ai.offset, std::addressof(name), name_size);
|
||||
rexy::memcpy(packed_data.data()+ai.offset+name_size, std::addressof(st), sizeof(st));
|
||||
|
||||
//setup next entry's offset
|
||||
if(argnum+1 < num_args){
|
||||
packed_data.info()[argnum+1].offset = ai.offset + sizeof(st) + name_size;
|
||||
}
|
||||
}
|
||||
template<class Context, class... Args>
|
||||
template<class... SArgs>
|
||||
void basic_format_arg_store<Context,Args...>::store_args(SArgs&&... args){
|
||||
std::size_t argnum = 0;
|
||||
(store_arg(argnum++, std::forward<SArgs>(args)), ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
100
include/rexy/detail/format/basic_types.hpp
Normal file
100
include/rexy/detail/format/basic_types.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
|
||||
#define REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include "storage.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
static constexpr std::size_t invalid_arg_index = std::size_t(-1);
|
||||
|
||||
|
||||
template<class Char>
|
||||
struct format_string_view{
|
||||
const Char* name;
|
||||
std::size_t length;
|
||||
};
|
||||
|
||||
struct arg_info{
|
||||
storage_type type = storage_type::none_t;
|
||||
std::size_t offset = 0;
|
||||
bool named = false;
|
||||
};
|
||||
|
||||
enum class alignment : int{
|
||||
none = 0,
|
||||
left = '<',
|
||||
right = '>',
|
||||
center = '^'
|
||||
};
|
||||
enum class presentation{
|
||||
default_t,
|
||||
int_t,
|
||||
char_t,
|
||||
float_t,
|
||||
string_t,
|
||||
ptr_t
|
||||
};
|
||||
|
||||
struct format_specs{
|
||||
int width = 0;
|
||||
int precision = 0;
|
||||
int type = 0;
|
||||
alignment align = alignment::none;
|
||||
presentation present = presentation::default_t;
|
||||
int align_char = ' ';
|
||||
int sign = 0;
|
||||
bool locale = false;
|
||||
bool alt_form = false;
|
||||
bool zero_fill = false;
|
||||
};
|
||||
enum class dyn_type{
|
||||
none = 0,
|
||||
index,
|
||||
string
|
||||
};
|
||||
template<class Char>
|
||||
struct dynamic_format_specs : public format_specs{
|
||||
union dyn_val{
|
||||
std::size_t index = invalid_arg_index;
|
||||
format_string_view<Char> name;
|
||||
}dyn_width, dyn_precision;
|
||||
|
||||
dyn_type width_type = dyn_type::index;
|
||||
dyn_type precision_type = dyn_type::index;
|
||||
};
|
||||
template<class FormatCtx>
|
||||
constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs<typename FormatCtx::char_type>& specs);
|
||||
|
||||
}
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class OutIt>
|
||||
struct format_to_n_result{
|
||||
OutIt out;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
55
include/rexy/detail/format/basic_types.tpp
Normal file
55
include/rexy/detail/format/basic_types.tpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
|
||||
#define REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
|
||||
|
||||
#include "basic_types.hpp"
|
||||
|
||||
#include "format_args.hpp"
|
||||
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
template<class FormatCtx>
|
||||
constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs<typename FormatCtx::char_type>& specs){
|
||||
std::size_t index = 0;
|
||||
if(specs.width_type == dyn_type::index){
|
||||
index = specs.dyn_width.index;
|
||||
}else{
|
||||
const auto& name = specs.dyn_width.name;
|
||||
index = ctx.arg_index(name.name, name.name + name.length);
|
||||
}
|
||||
specs.width = ((index != invalid_arg_index)
|
||||
? visit_format_arg(parse::dynamic_integer_retriever{}, ctx.arg(index))
|
||||
: specs.width);
|
||||
if(specs.precision_type == dyn_type::index){
|
||||
index = specs.dyn_precision.index;
|
||||
}else{
|
||||
const auto& name = specs.dyn_precision.name;
|
||||
index = ctx.arg_index(name.name, name.name + name.length);
|
||||
}
|
||||
specs.precision = ((index != invalid_arg_index)
|
||||
? visit_format_arg(parse::dynamic_integer_retriever{}, ctx.arg(index))
|
||||
: specs.precision);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
97
include/rexy/detail/format/context_handler.hpp
Normal file
97
include/rexy/detail/format/context_handler.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
|
||||
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include "standard_types.hpp"
|
||||
#include "internal_types.hpp"
|
||||
#include "format_context.hpp"
|
||||
#include "parse_context.hpp"
|
||||
#include "format_args.hpp"
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
#include <locale> //locale
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
//Holds the format and parse contexts and performs parse+format calls for replacement fields
|
||||
template<class Char>
|
||||
struct format_handler
|
||||
{
|
||||
public:
|
||||
using char_type = Char;
|
||||
using fmt_ctx_t = fmt_context_t<char_type>;
|
||||
using parse_ctx_t = parse_context_t<char_type>;
|
||||
using out_it_t = output_iterator_t<char_type>;
|
||||
using fmt_it_t = typename parse_ctx_t::iterator;
|
||||
using handle_t = typename basic_format_arg<fmt_ctx_t>::handle;
|
||||
template<class T>
|
||||
using fmter_t = typename fmt_ctx_t::template formatter_type<T>;
|
||||
|
||||
public:
|
||||
fmt_ctx_t fmt_ctx;
|
||||
parse_ctx_t parse_ctx;
|
||||
|
||||
public:
|
||||
format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args);
|
||||
format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l);
|
||||
constexpr fmt_it_t do_raw(fmt_it_t start, fmt_it_t fin);
|
||||
constexpr fmt_it_t do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id);
|
||||
constexpr fmt_it_t do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id);
|
||||
|
||||
constexpr std::size_t on_arg_id(void);
|
||||
constexpr std::size_t on_arg_id(std::size_t i);
|
||||
constexpr std::size_t on_arg_id(fmt_it_t start, fmt_it_t last);
|
||||
};
|
||||
template<class Char>
|
||||
format_handler(output_iterator_t<Char>, basic_string_view<Char>, basic_format_args<fmt_context_t<Char>>) -> format_handler<Char>;
|
||||
|
||||
//Constant-evaluated version of format_handler. Does not perform formatting but will complete a parsing
|
||||
//pass during compile
|
||||
template<class Char, class... Args>
|
||||
class format_checker
|
||||
{
|
||||
public:
|
||||
using char_type = Char;
|
||||
using parse_ctx_t = parse_context_t<char_type>;
|
||||
using fmt_it_t = typename parse_ctx_t::iterator;
|
||||
|
||||
parse_ctx_t parse_ctx;
|
||||
|
||||
consteval format_checker(void) = default;
|
||||
consteval format_checker(basic_string_view<char_type> fmt, std::size_t numargs = 0);
|
||||
constexpr fmt_it_t do_raw(fmt_it_t, fmt_it_t last);
|
||||
constexpr fmt_it_t do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id);
|
||||
constexpr void do_empty_format(fmt_it_t, fmt_it_t, std::size_t);
|
||||
|
||||
constexpr std::size_t on_arg_id(void);
|
||||
constexpr std::size_t on_arg_id(std::size_t i);
|
||||
constexpr std::size_t on_arg_id(fmt_it_t start, fmt_it_t last);
|
||||
|
||||
private:
|
||||
template<std::size_t I, class FArg, class... FArgs>
|
||||
constexpr fmt_it_t do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
162
include/rexy/detail/format/context_handler.tpp
Normal file
162
include/rexy/detail/format/context_handler.tpp
Normal file
@ -0,0 +1,162 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
|
||||
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
|
||||
|
||||
#include "context_handler.hpp"
|
||||
|
||||
#include "parse_context.hpp"
|
||||
#include "format_context.hpp"
|
||||
#include "formatter.hpp"
|
||||
#include "format_args.hpp"
|
||||
#include "named_args.hpp"
|
||||
|
||||
#include "format_error.hpp"
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
#include <locale> //locale
|
||||
#include <cstddef> //size_t
|
||||
#include <type_traits> //remove_cvref
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
///////////////////////////////format_handler//////////////////////////////////
|
||||
template<class Char>
|
||||
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args):
|
||||
fmt_ctx(args, output),
|
||||
parse_ctx(fmt){}
|
||||
template<class Char>
|
||||
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l):
|
||||
fmt_ctx(args, output, l),
|
||||
parse_ctx(fmt){}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto format_handler<Char>::do_raw(fmt_it_t start, fmt_it_t fin) -> fmt_it_t{
|
||||
for(auto it = start;it != fin;++it){
|
||||
fmt_ctx.out() = *it;
|
||||
}
|
||||
return fin;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto format_handler<Char>::do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id) -> fmt_it_t{
|
||||
parse_ctx.advance_to(start);
|
||||
const auto arg = fmt_ctx.arg(id);
|
||||
format_specs specs;
|
||||
visit_format_arg(format::arg_formatter<fmt_ctx_t,parse_ctx_t,format_specs>{fmt_ctx, parse_ctx, specs}, arg);
|
||||
return parse_ctx.begin();
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto format_handler<Char>::do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
|
||||
parse_ctx.advance_to(start);
|
||||
const auto arg = fmt_ctx.arg(id);
|
||||
visit_format_arg(format::empty_formatter<fmt_ctx_t,parse_ctx_t>{fmt_ctx, parse_ctx}, arg);
|
||||
return parse_ctx.begin();
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr std::size_t format_handler<Char>::on_arg_id(void){
|
||||
return parse_ctx.next_arg_id();
|
||||
}
|
||||
template<class Char>
|
||||
constexpr std::size_t format_handler<Char>::on_arg_id(std::size_t i){
|
||||
parse_ctx.check_arg_id(i);
|
||||
return i;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr std::size_t format_handler<Char>::on_arg_id(fmt_it_t start, fmt_it_t last){
|
||||
const auto id = fmt_ctx.arg_index(start, last);
|
||||
parse_ctx.check_arg_id(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////format_checker//////////////////////////////////
|
||||
template<class Char, class... Args>
|
||||
consteval format_checker<Char,Args...>::format_checker(basic_string_view<char_type> fmt, std::size_t numargs):
|
||||
parse_ctx(fmt, numargs){}
|
||||
template<class Char, class... Args>
|
||||
constexpr auto format_checker<Char,Args...>::do_raw(fmt_it_t, fmt_it_t last) -> fmt_it_t{
|
||||
return last;
|
||||
}
|
||||
template<class Char, class... Args>
|
||||
constexpr auto format_checker<Char,Args...>::do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
|
||||
parse_ctx.advance_to(start);
|
||||
if constexpr(sizeof...(Args)){
|
||||
return do_format_spec_impl<0,Args...>(start, last, id);
|
||||
}else{
|
||||
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||
return start;
|
||||
}
|
||||
}
|
||||
template<class Char, class... Args>
|
||||
constexpr void format_checker<Char,Args...>::do_empty_format(fmt_it_t, fmt_it_t, std::size_t){/*nothing to parse and the checker doesn't format so just return*/}
|
||||
template<class Char, class... Args>
|
||||
template<std::size_t I, class FArg, class... FArgs>
|
||||
constexpr auto format_checker<Char,Args...>::do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
|
||||
if(I == id){
|
||||
using fmt_ctx_t = fmt_context_t<Char>;
|
||||
using base_type = std::remove_cvref_t<FArg>;
|
||||
using stored_type = stored_type_t<base_type,fmt_ctx_t>;
|
||||
if constexpr(Handle<stored_type,fmt_ctx_t>){
|
||||
using fmter_t = typename fmt_ctx_t::template formatter_type<base_type>;
|
||||
fmter_t fmter{};
|
||||
return fmter.parse(parse_ctx);
|
||||
}else{
|
||||
dynamic_format_specs<Char> specs{};
|
||||
format_specs_checker<cx_format_specs_handler<parse_ctx_t,Args...>> handler{
|
||||
cx_format_specs_handler<parse_ctx_t,Args...>{parse_ctx, specs},
|
||||
detail::map_to_storage_enum_v<stored_type,Char>
|
||||
};
|
||||
return parse::perform_standard_parse(start, last, handler);
|
||||
}
|
||||
}else{
|
||||
if constexpr(sizeof...(FArgs) > 0){
|
||||
return do_format_spec_impl<I+1,FArgs...>(start, last, id);
|
||||
}
|
||||
}
|
||||
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||
}
|
||||
|
||||
template<class Char, class... Args>
|
||||
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(void){
|
||||
return parse_ctx.next_arg_id();
|
||||
}
|
||||
template<class Char, class... Args>
|
||||
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(std::size_t i){
|
||||
if(i > sizeof...(Args)){
|
||||
REXY_THROW_FORMAT_ERROR("Arg index out of bounds");
|
||||
}
|
||||
parse_ctx.check_arg_id(i);
|
||||
return i;
|
||||
}
|
||||
template<class Char, class... Args>
|
||||
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(fmt_it_t start, fmt_it_t last){
|
||||
const std::size_t id = find_static_named_arg_id<fmt_it_t, Args...>(start, last);
|
||||
if(id == invalid_arg_index){
|
||||
REXY_THROW_FORMAT_ERROR("No such named arg");
|
||||
}
|
||||
return on_arg_id(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
130
include/rexy/detail/format/format_args.hpp
Normal file
130
include/rexy/detail/format/format_args.hpp
Normal file
@ -0,0 +1,130 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include <variant> //monostate
|
||||
|
||||
#include "storage.hpp" //storage_type
|
||||
#include "basic_types.hpp" //arg_info
|
||||
#include "arg_store.hpp"
|
||||
#include "traits.hpp"
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
//Type erasure of single argument.
|
||||
//Context is an instantiation of 'basic_format_context'
|
||||
template<class Context>
|
||||
class basic_format_arg
|
||||
{
|
||||
public:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
public:
|
||||
//Handles call to formatter for a user defined type
|
||||
//type erase the user defined values
|
||||
class handle
|
||||
{
|
||||
public:
|
||||
using format_ptr_t = void(handle::*)(basic_format_parse_context<char_type>&, Context&)const;
|
||||
public:
|
||||
const void* m_data;
|
||||
format_ptr_t m_format_impl;
|
||||
public:
|
||||
template<class T>
|
||||
explicit handle(T&& t);
|
||||
template<class T>
|
||||
void format_impl(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const;
|
||||
void format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const;
|
||||
};
|
||||
|
||||
union{
|
||||
std::monostate none_v = {};
|
||||
bool bool_v;
|
||||
char_type char_v;
|
||||
int int_v;
|
||||
unsigned int uint_v;
|
||||
long long long_long_v;
|
||||
unsigned long long ulong_long_v;
|
||||
float float_v;
|
||||
double double_v;
|
||||
long double long_double_v;
|
||||
const char_type* char_ptr_v;
|
||||
basic_string_view<char_type> string_v;
|
||||
const void* ptr_v;
|
||||
handle custom_v;
|
||||
};
|
||||
detail::storage_type active_member = detail::storage_type::none_t;
|
||||
|
||||
//initialize std::variant equivalent to std::monostate
|
||||
basic_format_arg(void)noexcept{}
|
||||
|
||||
explicit basic_format_arg(bool b)noexcept;
|
||||
explicit basic_format_arg(char_type b)noexcept;
|
||||
explicit basic_format_arg(int b)noexcept;
|
||||
explicit basic_format_arg(unsigned int b)noexcept;
|
||||
explicit basic_format_arg(long long b)noexcept;
|
||||
explicit basic_format_arg(unsigned long long b)noexcept;
|
||||
explicit basic_format_arg(float b)noexcept;
|
||||
explicit basic_format_arg(double b)noexcept;
|
||||
explicit basic_format_arg(long double b)noexcept;
|
||||
explicit basic_format_arg(const char_type* b)noexcept;
|
||||
explicit basic_format_arg(basic_string_view<char_type> b)noexcept;
|
||||
explicit basic_format_arg(const void* b)noexcept;
|
||||
explicit basic_format_arg(const handle b)noexcept;
|
||||
|
||||
detail::storage_type type(void)const;
|
||||
|
||||
explicit operator bool(void)const noexcept;
|
||||
};
|
||||
|
||||
//Holds list of arguments, erasing concrete types.
|
||||
//Context is an instantiation of 'basic_format_context'
|
||||
template<class Context>
|
||||
class basic_format_args
|
||||
{
|
||||
public:
|
||||
using char_type = detail::extract_char_type_from_context_t<Context>;
|
||||
private:
|
||||
//TODO less pointers
|
||||
const detail::arg_info* m_info = nullptr;
|
||||
const unsigned char* m_data = nullptr;
|
||||
const std::size_t m_num_args = 0;
|
||||
|
||||
public:
|
||||
basic_format_args(void)noexcept = default;
|
||||
|
||||
constexpr basic_format_args(const detail::basic_format_arg_store<Context>& store)noexcept;
|
||||
template<class... Args>
|
||||
basic_format_args(const detail::basic_format_arg_store<Context,Args...>& store)noexcept;
|
||||
|
||||
basic_format_arg<Context> get(std::size_t i)const noexcept;
|
||||
basic_format_arg<Context> get(const char_type* first, const char_type* last)const;
|
||||
std::size_t get_index(const char_type* first, const char_type* last)const;
|
||||
};
|
||||
|
||||
//Visitor for basic_format_arg
|
||||
template<class Fun, class Context>
|
||||
decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg<Context>& arg);
|
||||
}
|
||||
|
||||
#endif
|
||||
236
include/rexy/detail/format/format_args.tpp
Normal file
236
include/rexy/detail/format/format_args.tpp
Normal file
@ -0,0 +1,236 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_ARGS_TPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_ARGS_TPP
|
||||
|
||||
#include "format_args.hpp"
|
||||
|
||||
#include "basic_types.hpp" //format_string_view
|
||||
#include "standard_types.hpp"
|
||||
#include "parse_context.hpp"
|
||||
#include "format_context.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "arg_store.hpp"
|
||||
#include "format_error.hpp"
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
#include <utility> //forward
|
||||
#include <type_traits> //remove_cvref, add_lvalue_reference, add_pointer, add_const, conditional
|
||||
#include <cstddef> //size_t
|
||||
#include <memory> //addressof
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
/////////////////////////////basic_format_arg::handle//////////////////////////////
|
||||
template<class Context>
|
||||
template<class T>
|
||||
basic_format_arg<Context>::handle::handle(T&& t):
|
||||
m_data(std::addressof(t)),
|
||||
m_format_impl(&handle::format_impl<std::remove_cvref_t<T>>){}
|
||||
template<class Context>
|
||||
template<class T>
|
||||
void basic_format_arg<Context>::handle::format_impl(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const{
|
||||
using value_type = std::remove_cvref_t<T>;
|
||||
using formatter_t = typename Context::template formatter_type<value_type>;
|
||||
using qualified_type = std::conditional_t<detail::has_const_formatter<value_type,Context>,
|
||||
std::add_const_t<value_type>,
|
||||
value_type>;
|
||||
using reference_type = std::add_lvalue_reference_t<qualified_type>;
|
||||
using pointer_type = std::add_pointer_t<qualified_type>;
|
||||
using const_pointer_type = std::add_pointer_t<std::add_const_t<qualified_type>>;
|
||||
|
||||
reference_type ref = *const_cast<pointer_type>(reinterpret_cast<const_pointer_type>(m_data));
|
||||
|
||||
formatter_t fmt;
|
||||
parse_ctx.advance_to(fmt.parse(parse_ctx));
|
||||
format_ctx.advance_to(fmt.format(ref, format_ctx));
|
||||
}
|
||||
|
||||
template<class Context>
|
||||
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const{
|
||||
//Pretty epic gamer moment here
|
||||
(this->*m_format_impl)(parse_ctx, format_ctx);
|
||||
}
|
||||
|
||||
///////////////////////////////basic_format_arg////////////////////////////////
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(bool b)noexcept:
|
||||
bool_v(b),
|
||||
active_member(detail::storage_type::bool_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(char_type b)noexcept:
|
||||
char_v(b),
|
||||
active_member(detail::storage_type::char_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(int b)noexcept:
|
||||
int_v(b),
|
||||
active_member(detail::storage_type::int_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(unsigned int b)noexcept:
|
||||
uint_v(b),
|
||||
active_member(detail::storage_type::uint_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(long long b)noexcept:
|
||||
long_long_v(b),
|
||||
active_member(detail::storage_type::long_long_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(unsigned long long b)noexcept:
|
||||
ulong_long_v(b),
|
||||
active_member(detail::storage_type::ulong_long_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(float b)noexcept:
|
||||
float_v(b),
|
||||
active_member(detail::storage_type::float_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(double b)noexcept:
|
||||
double_v(b),
|
||||
active_member(detail::storage_type::double_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(long double b)noexcept:
|
||||
long_double_v(b),
|
||||
active_member(detail::storage_type::long_double_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(const char_type* b)noexcept:
|
||||
char_ptr_v(b),
|
||||
active_member(detail::storage_type::char_ptr_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type> b)noexcept:
|
||||
string_v(b),
|
||||
active_member(detail::storage_type::string_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(const void* b)noexcept:
|
||||
ptr_v(b),
|
||||
active_member(detail::storage_type::ptr_t){}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg(const handle b)noexcept:
|
||||
custom_v(b),
|
||||
active_member(detail::storage_type::custom_t){}
|
||||
template<class Context>
|
||||
detail::storage_type basic_format_arg<Context>::type(void)const{
|
||||
return active_member;
|
||||
}
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::operator bool(void)const noexcept{
|
||||
return active_member != detail::storage_type::none_t;
|
||||
}
|
||||
|
||||
///////////////////////////////basic_format_args////////////////////////////////
|
||||
template<class Context>
|
||||
constexpr basic_format_args<Context>::basic_format_args(const detail::basic_format_arg_store<Context>&)noexcept{}
|
||||
template<class Context>
|
||||
template<class... Args>
|
||||
basic_format_args<Context>::basic_format_args(const detail::basic_format_arg_store<Context,Args...>& store)noexcept:
|
||||
m_info(store.packed_data.info()),
|
||||
m_data(store.packed_data.data()),
|
||||
m_num_args(sizeof...(Args)){}
|
||||
|
||||
template<class Context>
|
||||
basic_format_arg<Context> basic_format_args<Context>::get(std::size_t i)const noexcept{
|
||||
const bool named = m_info[i].named;
|
||||
const std::size_t offset = m_info[i].offset + (named ? sizeof(detail::format_string_view<char_type>) : 0);
|
||||
const unsigned char* const data = m_data + offset;
|
||||
|
||||
switch(m_info[i].type){
|
||||
case detail::storage_type::bool_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const bool*>(data)};
|
||||
case detail::storage_type::char_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const char_type*>(data)};
|
||||
case detail::storage_type::int_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const int*>(data)};
|
||||
case detail::storage_type::uint_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const unsigned int*>(data)};
|
||||
case detail::storage_type::long_long_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const long long*>(data)};
|
||||
case detail::storage_type::ulong_long_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const unsigned long long*>(data)};
|
||||
case detail::storage_type::float_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const float*>(data)};
|
||||
case detail::storage_type::double_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const double*>(data)};
|
||||
case detail::storage_type::long_double_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const long double*>(data)};
|
||||
case detail::storage_type::char_ptr_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const char_type* const*>(data)};
|
||||
case detail::storage_type::string_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const basic_string_view<char_type>*>(data)};
|
||||
case detail::storage_type::ptr_t:
|
||||
return basic_format_arg<Context>{reinterpret_cast<const void*>(*reinterpret_cast<const char* const*>(data))};
|
||||
case detail::storage_type::custom_t:
|
||||
return basic_format_arg<Context>{*reinterpret_cast<const typename basic_format_arg<Context>::handle*>(data)};
|
||||
default:
|
||||
return basic_format_arg<Context>{};
|
||||
};
|
||||
}
|
||||
template<class Context>
|
||||
basic_format_arg<Context> basic_format_args<Context>::get(const char_type* first, const char_type* last)const{
|
||||
return get(get_index(first, last));
|
||||
}
|
||||
template<class Context>
|
||||
std::size_t basic_format_args<Context>::get_index(const char_type* first, const char_type* last)const{
|
||||
for(std::size_t i = 0;i < m_num_args;++i){
|
||||
if(m_info[i].named){
|
||||
auto data = reinterpret_cast<const detail::format_string_view<char_type>* const>(m_data + m_info[i].offset);
|
||||
const std::size_t len = last - first;
|
||||
if(len == data->length && !rexy::strncmp(data->name, first, len)){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
REXY_THROW_FORMAT_ERROR("Requested named arg does not exist");
|
||||
}
|
||||
|
||||
|
||||
template<class Fun, class Context>
|
||||
decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg<Context>& arg){
|
||||
switch(arg.type()){
|
||||
case detail::storage_type::bool_t:
|
||||
return std::forward<Fun>(fun)(arg.bool_v);
|
||||
case detail::storage_type::char_t:
|
||||
return std::forward<Fun>(fun)(arg.char_v);
|
||||
case detail::storage_type::int_t:
|
||||
return std::forward<Fun>(fun)(arg.int_v);
|
||||
case detail::storage_type::uint_t:
|
||||
return std::forward<Fun>(fun)(arg.uint_v);
|
||||
case detail::storage_type::long_long_t:
|
||||
return std::forward<Fun>(fun)(arg.long_long_v);
|
||||
case detail::storage_type::ulong_long_t:
|
||||
return std::forward<Fun>(fun)(arg.ulong_long_v);
|
||||
case detail::storage_type::float_t:
|
||||
return std::forward<Fun>(fun)(arg.float_v);
|
||||
case detail::storage_type::double_t:
|
||||
return std::forward<Fun>(fun)(arg.double_v);
|
||||
case detail::storage_type::long_double_t:
|
||||
return std::forward<Fun>(fun)(arg.long_double_v);
|
||||
case detail::storage_type::char_ptr_t:
|
||||
return std::forward<Fun>(fun)(arg.char_ptr_v);
|
||||
case detail::storage_type::string_t:
|
||||
return std::forward<Fun>(fun)(arg.string_v);
|
||||
case detail::storage_type::ptr_t:
|
||||
return std::forward<Fun>(fun)(arg.ptr_v);
|
||||
case detail::storage_type::custom_t:
|
||||
return std::forward<Fun>(fun)(arg.custom_v);
|
||||
default:
|
||||
REXY_THROW_FORMAT_ERROR("Invalid format_arg storage_type");
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
77
include/rexy/detail/format/format_context.hpp
Normal file
77
include/rexy/detail/format/format_context.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_CONTEXT_HPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_CONTEXT_HPP
|
||||
|
||||
#include <type_traits> //remove_cvref, declval, integral_constant, void_t
|
||||
#include <cstddef> //size_t
|
||||
#include <locale> //locale
|
||||
|
||||
#include "standard_types.hpp" //basic_format_args, formatter
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
namespace detail{
|
||||
|
||||
//type trait used for properly cv-qualifying the argument to a call to formatter<type>::format
|
||||
template<class T, class Context, class = void>
|
||||
struct is_formatter_const : public std::false_type{};
|
||||
template<class T, class Context>
|
||||
struct is_formatter_const<T,Context,std::void_t<
|
||||
decltype(std::declval<typename Context::template formatter_type<std::remove_cvref_t<T>>>().format(std::declval<const T&>(), std::declval<Context>()))
|
||||
>> : public std::true_type{};
|
||||
template<class T, class Context>
|
||||
concept has_formatter = requires(typename Context::template formatter_type<std::remove_cvref_t<T>> fmter, T& t, Context& ctx){
|
||||
fmter.format(t, ctx);
|
||||
};
|
||||
template<class T, class Context>
|
||||
concept has_const_formatter = has_formatter<const std::remove_cvref_t<T>, Context>;
|
||||
|
||||
}
|
||||
|
||||
//Holds the arguments passed to calls to 'format' or 'vformat' in type erasing structure.
|
||||
//Additionally holds the iterator where output is written.
|
||||
template<class OutIt, class Char>
|
||||
class basic_format_context
|
||||
{
|
||||
public:
|
||||
using iterator = OutIt;
|
||||
using char_type = Char;
|
||||
template<class T>
|
||||
using formatter_type = formatter<T, Char>;
|
||||
|
||||
private:
|
||||
basic_format_args<basic_format_context> m_fmt_args;
|
||||
iterator m_outit;
|
||||
std::locale m_locale;
|
||||
|
||||
public:
|
||||
basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit);
|
||||
basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit, const std::locale& l);
|
||||
basic_format_arg<basic_format_context> arg(std::size_t id)const;
|
||||
basic_format_arg<basic_format_context> arg(const char_type* first, const char_type* last)const;
|
||||
std::size_t arg_index(const char_type* first, const char_type* last)const;
|
||||
std::locale locale(void);
|
||||
iterator out(void);
|
||||
void advance_to(iterator it);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
70
include/rexy/detail/format/format_context.tpp
Normal file
70
include/rexy/detail/format/format_context.tpp
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_CONTEXT_TPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_CONTEXT_TPP
|
||||
|
||||
#include "format_context.hpp"
|
||||
|
||||
#include "format_args.hpp"
|
||||
|
||||
#include <utility> //move
|
||||
#include <cstddef> //size_t
|
||||
#include <locale> //locale
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
///////////////////////////////basic_format_context////////////////////////////////
|
||||
template<class OutIt, class Char>
|
||||
basic_format_context<OutIt,Char>::basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit):
|
||||
m_fmt_args(fmt_args),
|
||||
m_outit(outit){}
|
||||
template<class OutIt, class Char>
|
||||
basic_format_context<OutIt,Char>::basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit, const std::locale& l):
|
||||
m_fmt_args(fmt_args),
|
||||
m_outit(outit),
|
||||
m_locale(l){}
|
||||
template<class OutIt, class Char>
|
||||
auto basic_format_context<OutIt,Char>::arg(std::size_t id)const -> basic_format_arg<basic_format_context>{
|
||||
return m_fmt_args.get(id);
|
||||
}
|
||||
template<class OutIt, class Char>
|
||||
auto basic_format_context<OutIt,Char>::arg(const char_type* first, const char_type* last)const -> basic_format_arg<basic_format_context>{
|
||||
return m_fmt_args.get(first, last);
|
||||
}
|
||||
template<class OutIt, class Char>
|
||||
std::size_t basic_format_context<OutIt,Char>::arg_index(const char_type* first, const char_type* last)const{
|
||||
return m_fmt_args.get_index(first, last);
|
||||
}
|
||||
|
||||
template<class OutIt, class Char>
|
||||
std::locale basic_format_context<OutIt,Char>::locale(void){
|
||||
return m_locale;
|
||||
}
|
||||
template<class OutIt, class Char>
|
||||
auto basic_format_context<OutIt,Char>::out(void) -> iterator{
|
||||
return std::move(m_outit);
|
||||
}
|
||||
template<class OutIt, class Char>
|
||||
void basic_format_context<OutIt,Char>::advance_to(iterator it){
|
||||
m_outit = std::move(it);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
47
include/rexy/detail/format/format_error.hpp
Normal file
47
include/rexy/detail/format/format_error.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_ERROR_HPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_ERROR_HPP
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
#include "../../detail/debug_config.hpp"
|
||||
|
||||
#define REXY_FORMAT_REAL_STRINGIFY(str) #str
|
||||
#define REXY_FORMAT_STRINGIFY(str) REXY_FORMAT_REAL_STRINGIFY(str)
|
||||
#if LIBREXY_ENABLE_DEBUG_LEVEL > 0
|
||||
#define REXY_THROW_FORMAT_ERROR(errstring) throw format_error{__FILE__ ":" REXY_FORMAT_STRINGIFY(__LINE__) ": " errstring}
|
||||
#else
|
||||
#define REXY_THROW_FORMAT_ERROR(errstring) throw format_error{errstring}
|
||||
#endif
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
struct format_error{
|
||||
string_view errstr;
|
||||
|
||||
constexpr const char* what(void)const{
|
||||
return errstr.c_str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,29 +16,23 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_STRING_HASH_HPP
|
||||
#define REXY_STRING_HASH_HPP
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
|
||||
|
||||
#include "hash.hpp"
|
||||
#include "rexy.hpp"
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
namespace rexy{
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
//jenkns one at a time hash
|
||||
template<class Str>
|
||||
struct string_hash{
|
||||
constexpr size_t operator()(const Str& s, size_t salt)const noexcept{
|
||||
size_t hash = salt;
|
||||
for(size_t i = 0;i < s.length();++i){
|
||||
hash += s[i];
|
||||
hash += (hash << 10);
|
||||
hash += (hash >> 6);
|
||||
}
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash << 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
//Class accepted as an argument to 'format' calls. Will call a constant-evaluated parse of the format string
|
||||
//to check for correctness.
|
||||
template<class Char, class... Args>
|
||||
class basic_format_string
|
||||
{
|
||||
public:
|
||||
basic_string_view<Char> str;
|
||||
|
||||
consteval basic_format_string(const Char* f);
|
||||
consteval basic_format_string(basic_string_view<Char> f);
|
||||
};
|
||||
|
||||
}
|
||||
43
include/rexy/detail/format/format_string.tpp
Normal file
43
include/rexy/detail/format/format_string.tpp
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMAT_STRING_TPP
|
||||
#define REXY_DETAIL_FORMAT_FORMAT_STRING_TPP
|
||||
|
||||
#include "format_string.hpp"
|
||||
|
||||
#include "context_handler.hpp"
|
||||
#include "parse.hpp"
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
template<class Char, class... Args>
|
||||
consteval basic_format_string<Char,Args...>::basic_format_string(const Char* f):
|
||||
basic_format_string(basic_string_view<Char>{f}){}
|
||||
template<class Char, class... Args>
|
||||
consteval basic_format_string<Char,Args...>::basic_format_string(basic_string_view<Char> f):
|
||||
str(f)
|
||||
{
|
||||
parse::perform_parse(format_checker<Char, Args...>{f, sizeof...(Args)}, str);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
178
include/rexy/detail/format/formatter.hpp
Normal file
178
include/rexy/detail/format/formatter.hpp
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMATTER_HPP
|
||||
#define REXY_DETAIL_FORMAT_FORMATTER_HPP
|
||||
|
||||
#include <cstddef> //std::size_t, nullptr_t
|
||||
#include <variant> //monostate
|
||||
#include <locale> //locale
|
||||
#include <type_traits> //remove_cvref
|
||||
#include <utility> //forward
|
||||
|
||||
#include "basic_types.hpp" //format_specs
|
||||
#include "internal_types.hpp"
|
||||
#include "standard_types.hpp" //basic_parse_context
|
||||
#include "specs_handler.hpp" //format_specs_checker
|
||||
#include "traits.hpp"
|
||||
|
||||
#include "../../string.hpp"
|
||||
#include "../../string_view.hpp"
|
||||
#include "../../allocator.hpp"
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
namespace detail{
|
||||
|
||||
namespace format{
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(const void* t, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(const void* t, OutIt out);
|
||||
template<class Char, class OutIt>
|
||||
requires(!UTF8_String<Char>)
|
||||
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<UTF8_String Char, class OutIt>
|
||||
requires(UTF8_String<Char>)
|
||||
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(const Char* c, OutIt out);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(Char b, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(Char b, OutIt out);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(bool b, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(bool b, OutIt out);
|
||||
template<class Char, class OutIt, Integral T>
|
||||
constexpr OutIt perform_standard_format(T b, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt, Arithmetic T>
|
||||
constexpr OutIt perform_standard_format(T b, OutIt out);
|
||||
template<class Char, class OutIt, Floating T>
|
||||
constexpr OutIt perform_standard_format(T f, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(std::monostate, OutIt out, const format_specs&, const std::locale&);
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(std::monostate, OutIt out);
|
||||
|
||||
template<class FmtCtx, class ParseCtx, class Specs>
|
||||
struct arg_formatter{
|
||||
FmtCtx& fmt_ctx;
|
||||
ParseCtx& parse_ctx;
|
||||
const Specs& specs;
|
||||
|
||||
template<Handle<FmtCtx> T>
|
||||
constexpr void operator()(T&& t);
|
||||
template<class T>
|
||||
constexpr void operator()(T&& t);
|
||||
};
|
||||
template<class FmtCtx, class ParseCtx>
|
||||
struct empty_formatter{
|
||||
FmtCtx& fmt_ctx;
|
||||
ParseCtx& parse_ctx;
|
||||
|
||||
template<Handle<FmtCtx> T>
|
||||
constexpr void operator()(T&& t);
|
||||
template<class T>
|
||||
constexpr void operator()(T&& t);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template<class T, class Char>
|
||||
class formatter_base
|
||||
{
|
||||
public:
|
||||
using char_type = Char;
|
||||
using parse_ctx_t = basic_format_parse_context<char_type>;
|
||||
using fmt_ctx_t = detail::fmt_context_t<char_type>;
|
||||
using format_spec_t = detail::dynamic_format_specs<char_type>;
|
||||
using format_spec_handler_t = detail::format_specs_checker<detail::dynamic_format_specs_handler<parse_ctx_t>>;
|
||||
|
||||
protected:
|
||||
format_spec_t specs;
|
||||
|
||||
public:
|
||||
constexpr auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin());
|
||||
template<class U, class FormatContext>
|
||||
auto format(U&& t, FormatContext& ctx) -> decltype(ctx.out());
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace rexy{
|
||||
template<class T, class Char = char>
|
||||
class formatter;
|
||||
|
||||
#define IMPLEMENT_STANDARD_FORMATTER(type) \
|
||||
template<fmt::detail::Formatter_Char C> \
|
||||
class formatter<type, C> : public fmt::detail::formatter_base<fmt::detail::stored_type_t<type,fmt::detail::fmt_context_t<C>>, C>{}
|
||||
|
||||
IMPLEMENT_STANDARD_FORMATTER(int);
|
||||
IMPLEMENT_STANDARD_FORMATTER(unsigned int);
|
||||
IMPLEMENT_STANDARD_FORMATTER(long long);
|
||||
IMPLEMENT_STANDARD_FORMATTER(unsigned long long);
|
||||
IMPLEMENT_STANDARD_FORMATTER(bool);
|
||||
IMPLEMENT_STANDARD_FORMATTER(float);
|
||||
IMPLEMENT_STANDARD_FORMATTER(double);
|
||||
IMPLEMENT_STANDARD_FORMATTER(long double);
|
||||
IMPLEMENT_STANDARD_FORMATTER(std::nullptr_t);
|
||||
IMPLEMENT_STANDARD_FORMATTER(short);
|
||||
IMPLEMENT_STANDARD_FORMATTER(unsigned short);
|
||||
IMPLEMENT_STANDARD_FORMATTER(long);
|
||||
IMPLEMENT_STANDARD_FORMATTER(unsigned long);
|
||||
IMPLEMENT_STANDARD_FORMATTER(char);
|
||||
IMPLEMENT_STANDARD_FORMATTER(unsigned char);
|
||||
IMPLEMENT_STANDARD_FORMATTER(signed char);
|
||||
|
||||
template<fmt::detail::Formatter_Char C>
|
||||
class formatter<C*,C> : public fmt::detail::formatter_base<const C*,C>{};
|
||||
|
||||
template<fmt::detail::Formatter_Char C>
|
||||
class formatter<const C*,C> : public fmt::detail::formatter_base<const C*,C>{};
|
||||
|
||||
template<fmt::detail::Formatter_Char C, std::size_t I>
|
||||
class formatter<const C[I],C> : public fmt::detail::formatter_base<const C*,C>{};
|
||||
|
||||
template<fmt::detail::Formatter_Char C, REXY_ALLOCATOR_CONCEPT A>
|
||||
class formatter<basic_string<C,A>,C> : public fmt::detail::formatter_base<basic_string_view<C>,C>{};
|
||||
|
||||
template<fmt::detail::Formatter_Char C>
|
||||
class formatter<basic_string_view<C>,C> : public fmt::detail::formatter_base<basic_string_view<C>,C>{};
|
||||
|
||||
template<class T, fmt::detail::Formatter_Char C>
|
||||
class formatter<T*,C> : public fmt::detail::formatter_base<const void*,C>{};
|
||||
|
||||
template<class T, fmt::detail::Formatter_Char C>
|
||||
class formatter<const T*,C> : public fmt::detail::formatter_base<const void*,C>{};
|
||||
|
||||
|
||||
#undef IMPLEMENT_STANDARD_FORMATTER
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
665
include/rexy/detail/format/formatter.tpp
Normal file
665
include/rexy/detail/format/formatter.tpp
Normal file
@ -0,0 +1,665 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_FORMATTER_TPP
|
||||
#define REXY_DETAIL_FORMAT_FORMATTER_TPP
|
||||
|
||||
#include "formatter.hpp"
|
||||
|
||||
#include "basic_types.hpp"
|
||||
#include "internal_types.hpp"
|
||||
#include "standard_types.hpp"
|
||||
#include "parse_context.hpp"
|
||||
#include "format_context.hpp"
|
||||
#include "format_args.hpp"
|
||||
#include "parse.hpp"
|
||||
#include "utf_iterator.hpp"
|
||||
|
||||
#include "../../utility.hpp" //abs, memcpy
|
||||
|
||||
#include <type_traits> //remove_cvref
|
||||
#include <utility> //forward, move
|
||||
#include <cstddef> //size_t
|
||||
#include <algorithm> //max
|
||||
#include <cmath> //signbit
|
||||
#include <charconv> //to_chars
|
||||
#include <cctype> //toupper
|
||||
#include <cstdint> //uintptr_t
|
||||
#include <locale> //locale
|
||||
#include <variant> //monostate
|
||||
#include <limits>
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
template<class T, class Char>
|
||||
constexpr auto formatter_base<T,Char>::parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()){
|
||||
using stored_type = detail::stored_type_t<std::remove_cvref_t<T>,fmt_ctx_t>;
|
||||
return detail::parse::perform_standard_parse(
|
||||
ctx.begin(),
|
||||
ctx.end(),
|
||||
format_spec_handler_t{
|
||||
detail::dynamic_format_specs_handler<parse_ctx_t>{ctx, specs},
|
||||
detail::map_to_storage_enum_v<stored_type,char_type>
|
||||
}
|
||||
);
|
||||
}
|
||||
template<class T, class Char>
|
||||
template<class U, class FormatContext>
|
||||
auto formatter_base<T,Char>::format(U&& t, FormatContext& ctx) -> decltype(ctx.out()){
|
||||
normalize_dynamic_format_specs(ctx, specs);
|
||||
return detail::format::perform_standard_format<Char>(std::forward<U>(t), ctx.out(), specs);
|
||||
}
|
||||
|
||||
namespace format{
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_format_write(const Char* c, OutIt out, std::size_t length){
|
||||
for(std::size_t i = 0;i < length;++i){
|
||||
*out++ = c[i];
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
template<class OutIt, class Outputer>
|
||||
constexpr OutIt perform_format_write_aligned(OutIt out, int width, const format_specs& specs, alignment default_align, Outputer&& outputer){
|
||||
const int fill_amount = std::max(specs.width - width, 0);
|
||||
int left_fill_amount = fill_amount;
|
||||
int right_fill_amount = fill_amount;
|
||||
const alignment align = specs.align == alignment::none ? default_align : specs.align;
|
||||
|
||||
switch(align){
|
||||
case alignment::center:
|
||||
left_fill_amount /= 2;
|
||||
[[fallthrough]];
|
||||
case alignment::right:
|
||||
for(std::size_t i = 0;i < left_fill_amount;++i){
|
||||
*out++ = specs.align_char;
|
||||
}
|
||||
right_fill_amount -= left_fill_amount;
|
||||
[[fallthrough]];
|
||||
case alignment::left:
|
||||
out = outputer(std::move(out));
|
||||
|
||||
for(std::size_t i = 0;i < right_fill_amount;++i){
|
||||
*out++ = specs.align_char;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
REXY_THROW_FORMAT_ERROR("Invalid alignment state");
|
||||
};
|
||||
return std::move(out);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
consteval T cxlog10(T t){
|
||||
return t < 10 ? 1 : 1 + cxlog10(t / 10);
|
||||
}
|
||||
template<class OutIt, class T>
|
||||
constexpr OutIt perform_format_write_sign(OutIt out, int sign, T val){
|
||||
if(val < 0){
|
||||
*out++ = '-';
|
||||
}else if(sign == '+'){
|
||||
*out++ = '+';
|
||||
}else if(sign == ' '){
|
||||
*out++ = ' ';
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
template<class OutIt, Floating T>
|
||||
constexpr OutIt perform_format_write_sign(OutIt out, int sign, T val){
|
||||
if(std::signbit(val)){
|
||||
*out++ = '-';
|
||||
}else if(sign == '+'){
|
||||
*out++ = '+';
|
||||
}else if(sign == ' '){
|
||||
*out++ = ' ';
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(const void* t, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
//2 hexidecimal digits per octet
|
||||
constexpr std::size_t maxbufflen = sizeof(std::uintptr_t) * (CHAR_BIT / 4.0) + 3; //+2 for '0x', +1 for float rounding truncation
|
||||
|
||||
char buff[maxbufflen] = {};
|
||||
buff[0] = '0';
|
||||
buff[1] = 'x';
|
||||
char* buffstart = buff + 2;
|
||||
char* buffend = buff + maxbufflen;
|
||||
|
||||
auto result = std::to_chars(buffstart, buffend, reinterpret_cast<std::uintptr_t>(t), 16);
|
||||
if(result.ec != std::errc{}){
|
||||
REXY_THROW_FORMAT_ERROR("Unable to convert pointer type");
|
||||
}
|
||||
return perform_standard_format<Char>(buff, out, specs, loc);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(const void* t, OutIt out){
|
||||
//2 hexidecimal digits per octet
|
||||
constexpr std::size_t maxbufflen = sizeof(std::uintptr_t) * (CHAR_BIT / 4.0) + 4; //+2 for '0x', +1 for float rounding truncation
|
||||
|
||||
char buff[maxbufflen] = {};
|
||||
buff[0] = '0';
|
||||
buff[1] = 'x';
|
||||
char* buffstart = buff + 2;
|
||||
char* buffend = buff + maxbufflen;
|
||||
|
||||
auto result = std::to_chars(buffstart, buffend, reinterpret_cast<std::uintptr_t>(t), 16);
|
||||
if(result.ec != std::errc{}){
|
||||
REXY_THROW_FORMAT_ERROR("Unable to convert pointer type");
|
||||
}
|
||||
const auto bufflen = result.ptr - buff;
|
||||
return perform_format_write(buff, out, bufflen);
|
||||
}
|
||||
|
||||
|
||||
static constexpr unsigned int codepoint_fields[] = {
|
||||
0x1100u, 0x115Fu,
|
||||
0x2329u, 0x232Au,
|
||||
0x2E80u, 0x303Eu,
|
||||
0x3040u, 0xA4CFu,
|
||||
0xAC00u, 0xD7A3u,
|
||||
0xF900u, 0xFAFFu,
|
||||
0xFE10u, 0xFE19u,
|
||||
0xFE30u, 0xFE6Fu,
|
||||
0xFF00u, 0xFF60u,
|
||||
0xFFE0u, 0xFFE6u,
|
||||
0x1F300u, 0x1F64Fu,
|
||||
0x1F900u, 0x1F9FFu,
|
||||
0x20000u, 0x2FFFDu,
|
||||
0x30000u, 0x3FFFDu
|
||||
};
|
||||
constexpr unsigned int estimate_unicode_width(const char32_t c){
|
||||
unsigned int width = 1;
|
||||
for(auto field : codepoint_fields){
|
||||
if(c < field){
|
||||
return width;
|
||||
}
|
||||
//flip between 1 and 2
|
||||
width ^= 3u;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
constexpr unsigned int estimate_unicode_string_width(const char* first, const char* last){
|
||||
unsigned int width = 0;
|
||||
for(utf8_iterator<char> it{first, last};it.valid();++it){
|
||||
const auto codepoint = *it;
|
||||
width += estimate_unicode_width(codepoint);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
requires(!UTF8_String<Char>)
|
||||
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
const basic_string_view<Char> str{c};
|
||||
std::size_t est_width = 0;
|
||||
std::size_t print_cnt = 0;
|
||||
if(specs.precision > 0){
|
||||
est_width = std::min<std::size_t>(str.length(), specs.precision);
|
||||
print_cnt = est_width;
|
||||
}else{
|
||||
est_width = str.length();
|
||||
print_cnt = str.length();
|
||||
}
|
||||
|
||||
const auto outputter = [&](OutIt o){
|
||||
for(std::size_t i = 0;i < print_cnt;++i){
|
||||
*o++ = str[i];
|
||||
}
|
||||
return std::move(o);
|
||||
};
|
||||
|
||||
return perform_format_write_aligned(std::move(out), est_width, specs, alignment::left, outputter);
|
||||
}
|
||||
template<UTF8_String Char, class OutIt>
|
||||
requires(UTF8_String<Char>)
|
||||
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
const basic_string_view<Char> str{c};
|
||||
std::size_t est_width = 0;
|
||||
std::size_t print_cnt = 0;
|
||||
if(specs.precision > 0){
|
||||
for(utf8_iterator<Char> it{str.begin(), str.end()};it.valid();++it){
|
||||
const auto ch_width = estimate_unicode_width(*it);
|
||||
if(ch_width + est_width > specs.precision){
|
||||
break;
|
||||
}
|
||||
print_cnt += it.byte_count();
|
||||
est_width += ch_width;
|
||||
}
|
||||
}else{
|
||||
est_width = estimate_unicode_string_width(str.cbegin(), str.cend());
|
||||
print_cnt = str.length();
|
||||
}
|
||||
|
||||
const auto outputter = [&](OutIt o){
|
||||
for(std::size_t i = 0;i < print_cnt;++i){
|
||||
*o++ = str[i];
|
||||
}
|
||||
return std::move(o);
|
||||
};
|
||||
|
||||
return perform_format_write_aligned(std::move(out), est_width, specs, alignment::left, outputter);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(const Char* c, OutIt out){
|
||||
for(const Char* i = c;*i;++i){
|
||||
*out++ = *i;
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
return perform_standard_format<Char>(str.c_str(), std::move(out), specs, loc);
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out){
|
||||
return perform_standard_format(str.c_str(), std::move(out));
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(Char b, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
if(specs.present == presentation::int_t){
|
||||
return perform_standard_format<Char>(static_cast<int>(b), std::move(out), specs, loc);
|
||||
}
|
||||
const auto outputter = [=](OutIt o){
|
||||
*o++ = b;
|
||||
return std::move(o);
|
||||
};
|
||||
return perform_format_write_aligned(std::move(out), 1, specs, alignment::left, outputter);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(Char b, OutIt out){
|
||||
*out++ = b;
|
||||
return std::move(out);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(bool b, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
switch(specs.present){
|
||||
case presentation::default_t:
|
||||
case presentation::string_t:
|
||||
break;
|
||||
case presentation::int_t:
|
||||
case presentation::char_t:
|
||||
return perform_standard_format<Char>(static_cast<unsigned char>(b), std::move(out), specs, loc);
|
||||
default:
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type argument for bool");
|
||||
};
|
||||
|
||||
if(specs.locale){
|
||||
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||
const auto word = b ? facet.truename() : facet.falsename();
|
||||
format_specs copy_specs = specs;
|
||||
copy_specs.locale = false;
|
||||
return perform_standard_format<Char>(
|
||||
word.c_str(),
|
||||
std::move(out),
|
||||
copy_specs,
|
||||
loc
|
||||
);
|
||||
}
|
||||
|
||||
return perform_standard_format<Char>(b ? "true" : "false", std::move(out), specs, loc);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(bool b, OutIt out){
|
||||
return perform_standard_format<Char>(b ? "true" : "false", std::move(out));
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_localized_integer_write(OutIt out, const char* start, const char* last, const std::locale& loc){
|
||||
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||
const int group_size = int(facet.grouping()[0]);
|
||||
const Char sep = facet.thousands_sep();
|
||||
const int len = int(last - start);
|
||||
if(group_size != 0){
|
||||
int write_count = (len-1) % group_size;
|
||||
*out++ = *start++;
|
||||
while(true){
|
||||
for(;write_count > 0;--write_count){
|
||||
*out++ = *start++;
|
||||
}
|
||||
if(start == last){
|
||||
break;
|
||||
}
|
||||
*out++ = sep;
|
||||
write_count = std::min(group_size, int(last - start));
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
return perform_format_write(start, std::move(out), len);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt, Integral T>
|
||||
constexpr OutIt perform_standard_format(T b, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
constexpr std::size_t maxbufflen = rexy::max(
|
||||
std::numeric_limits<long long>::digits + 3, //add 1 for sign bit, 2 for prefix
|
||||
std::numeric_limits<unsigned long long>::digits + 3
|
||||
);
|
||||
if(specs.present == presentation::char_t){
|
||||
return perform_standard_format<Char>(static_cast<Char>(b), std::move(out), specs, loc);
|
||||
};
|
||||
|
||||
int base = 10;
|
||||
int total_width = 0;
|
||||
const bool should_zero_fill = specs.zero_fill && specs.align == alignment::none;
|
||||
bool to_upper = false;
|
||||
char buff[maxbufflen] = {};
|
||||
char* buffstart = buff;
|
||||
char* buffend = buff + maxbufflen;
|
||||
string_view prefix = "";
|
||||
|
||||
switch(specs.type){
|
||||
case 'b':
|
||||
prefix = "0b";
|
||||
base = 2;
|
||||
break;
|
||||
case 'B':
|
||||
prefix = "0B";
|
||||
base = 2;
|
||||
break;
|
||||
case 'o':
|
||||
prefix = b == 0 ? "" : "0";
|
||||
base = 8;
|
||||
break;
|
||||
case 'x':
|
||||
prefix = "0x";
|
||||
base = 16;
|
||||
break;
|
||||
case 'X':
|
||||
prefix = "0X";
|
||||
to_upper = true;
|
||||
base = 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
total_width += prefix.length();
|
||||
|
||||
auto result = std::to_chars(buffstart, buffend, b, base);
|
||||
if(result.ec != std::errc{}){
|
||||
REXY_THROW_FORMAT_ERROR("Unable to convert integral type");
|
||||
}
|
||||
buffend = result.ptr;
|
||||
if(b < 0){
|
||||
++buffstart;
|
||||
total_width += 1;
|
||||
}else if(specs.sign != 0){
|
||||
total_width += 1;
|
||||
}
|
||||
const auto bufflen = buffend - buffstart;
|
||||
total_width += bufflen;
|
||||
|
||||
if(to_upper){
|
||||
for(auto it = buffstart;it != buffend;++it){
|
||||
*it = std::toupper(*it);
|
||||
}
|
||||
}
|
||||
|
||||
const auto outputter = [&](OutIt o) -> OutIt{
|
||||
if(specs.alt_form){
|
||||
o = perform_format_write_sign(std::move(o), specs.sign, b);
|
||||
o = perform_format_write(prefix.data(), std::move(o), prefix.length());
|
||||
}else{
|
||||
o = perform_format_write(prefix.data(), std::move(out), prefix.length());
|
||||
o = perform_format_write_sign(std::move(o), specs.sign, b);
|
||||
}
|
||||
if(should_zero_fill && specs.width > total_width){
|
||||
const int fill_width = specs.width - total_width;
|
||||
for(int i = 0;i < fill_width;++i){
|
||||
*o++ = '0';
|
||||
}
|
||||
}
|
||||
if(specs.locale){
|
||||
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||
const int group_size = facet.grouping()[0];
|
||||
const Char sep = facet.thousands_sep();
|
||||
return perform_localized_integer_write<Char>(std::move(o), buffstart, buffend, loc);
|
||||
}
|
||||
|
||||
return perform_format_write(buffstart, std::move(o), bufflen);
|
||||
};
|
||||
|
||||
if(should_zero_fill){
|
||||
return outputter(std::move(out));
|
||||
}
|
||||
return perform_format_write_aligned(std::move(out), total_width, specs, alignment::right, outputter);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////DONE///////////////////////////////////////////////
|
||||
template<class Char, class OutIt, Arithmetic T>
|
||||
constexpr OutIt perform_standard_format(T b, OutIt out){
|
||||
using limits = std::numeric_limits<long double>;
|
||||
constexpr auto maxexp = limits::max_exponent10; //this is a constant expression but using limits::max_exponent10 directly isn't?
|
||||
//long double will be the longest type possible, so operate on that.
|
||||
//+4 for ones' place digit, decimal point, and 'e+' in scientific mode
|
||||
//maximum buffer length is the maximum significant digits plus maximum length of exponent
|
||||
constexpr std::size_t maxbufflen = 4 + limits::max_digits10 + cxlog10(maxexp);
|
||||
|
||||
char buff[maxbufflen] = {};
|
||||
char* buffend = buff + maxbufflen;
|
||||
|
||||
auto result = std::to_chars(buff, buffend, b);
|
||||
if(result.ec != std::errc{}){
|
||||
REXY_THROW_FORMAT_ERROR("Unable to convert arithmetic type");
|
||||
}
|
||||
|
||||
const auto bufflen = result.ptr - buff;
|
||||
return perform_format_write(buff, std::move(out), bufflen);
|
||||
}
|
||||
|
||||
//TODO
|
||||
template<class Char, class OutIt, Floating T>
|
||||
constexpr OutIt perform_standard_format(T f, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||
using limits = std::numeric_limits<T>;
|
||||
//max number of post-decimal digits is same as the inverted smallest radix exponent
|
||||
constexpr int max_precision = rexy::abs(limits::min_exponent);
|
||||
//max number of leading digits is same as biggest decimal exponent
|
||||
constexpr int max_significants = limits::max_exponent10;
|
||||
//+4 for ones' place digit, decimal point, and 'e+' in scientific mode
|
||||
//maximum buffer length is the maximum significant digits plus maximum precision because the
|
||||
//user can request any precision. So you can take the longest number with no decimal and add on
|
||||
//the longest decimal trail allowed.
|
||||
constexpr int maxbufflen = max_precision + max_significants + 4;
|
||||
|
||||
char buff[maxbufflen] = {};
|
||||
char* buffstart = buff;
|
||||
char* buffend = buff + maxbufflen;
|
||||
|
||||
const bool supplied_precision = specs.precision > 0;
|
||||
const bool is_infinity = f == std::numeric_limits<T>::infinity() || f == -std::numeric_limits<T>::infinity();
|
||||
const bool is_nan = (f != f);
|
||||
const bool is_integer_representable = (static_cast<T>(static_cast<long long>(f)) == f) && !is_infinity && !is_nan;
|
||||
const bool should_zero_fill = specs.zero_fill && specs.align == alignment::none;
|
||||
|
||||
std::chars_format fmt = std::chars_format::general;
|
||||
bool to_upper = false;
|
||||
bool manual_precision = supplied_precision;
|
||||
bool trailing_dot = false;
|
||||
std::size_t total_width = 0;
|
||||
|
||||
//TODO handle any other modes and formatting options
|
||||
std::to_chars_result result{};
|
||||
switch(specs.type){
|
||||
case 'A':
|
||||
to_upper = true;
|
||||
[[fallthrough]];
|
||||
case 'a':
|
||||
fmt = std::chars_format::hex;
|
||||
break;
|
||||
case 'E':
|
||||
to_upper = true;
|
||||
[[fallthrough]];
|
||||
case 'e':
|
||||
fmt = std::chars_format::scientific;
|
||||
manual_precision = true;
|
||||
break;
|
||||
case 'F':
|
||||
to_upper = true;
|
||||
[[fallthrough]];
|
||||
case 'f':
|
||||
fmt = std::chars_format::fixed;
|
||||
manual_precision = true;
|
||||
break;
|
||||
case 'G':
|
||||
to_upper = true;
|
||||
[[fallthrough]];
|
||||
case 'g':
|
||||
manual_precision = true;
|
||||
if(specs.alt_form){
|
||||
//keep trailing zeros
|
||||
fmt = std::chars_format::fixed;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
trailing_dot = is_integer_representable && !supplied_precision && specs.alt_form;
|
||||
break;
|
||||
};
|
||||
|
||||
//manually handle nan and inf since some compilers (msvc) output something other than the desired values in 'to_chars'
|
||||
if(is_nan){
|
||||
result.ptr = buffstart + 3;
|
||||
rexy::memcpy(buffstart, "nan", 3);
|
||||
}else if(is_infinity){
|
||||
result.ptr = buffstart + 3;
|
||||
rexy::memcpy(buffstart, "inf", 3);
|
||||
}else if(manual_precision){
|
||||
const int precision = supplied_precision ? specs.precision : 6;
|
||||
result = std::to_chars(buffstart, buffend, f, fmt, precision);
|
||||
}else{
|
||||
result = std::to_chars(buffstart, buffend, f, fmt);
|
||||
}
|
||||
|
||||
if(result.ec != std::errc{}){
|
||||
REXY_THROW_FORMAT_ERROR("Unable to convert floating type");
|
||||
}
|
||||
buffend = result.ptr;
|
||||
|
||||
//exclude negative sign automatically put there
|
||||
if(buffstart[0] == '-'){
|
||||
++buffstart;
|
||||
++total_width;
|
||||
}else if(specs.sign != 0){
|
||||
++total_width;
|
||||
}
|
||||
if(trailing_dot){
|
||||
++total_width;
|
||||
}
|
||||
|
||||
const auto bufflen = buffend - buffstart;
|
||||
total_width += bufflen;
|
||||
|
||||
if(to_upper){
|
||||
for(auto it = buffstart;it != buffend;++it){
|
||||
*it = std::toupper(*it);
|
||||
}
|
||||
}
|
||||
|
||||
const auto outputter = [&](OutIt o){
|
||||
Char radix_char = '.';
|
||||
o = perform_format_write_sign(std::move(o), specs.sign, f);
|
||||
if(should_zero_fill && specs.width > total_width && !(is_infinity || is_nan)){
|
||||
const int fill_width = specs.width - total_width;
|
||||
for(int i = 0;i < fill_width;++i){
|
||||
*o++ = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if(specs.locale){
|
||||
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||
const int group_size = facet.grouping()[0];
|
||||
radix_char = facet.decimal_point();
|
||||
string_view buff_view{buffstart, buffend};
|
||||
const auto radix_pos = buff_view.find_first_of('.');
|
||||
if(radix_pos != string_view::npos){
|
||||
buff[radix_pos] = radix_char;
|
||||
o = perform_localized_integer_write<Char>(std::move(o), buffstart, buffstart + radix_pos, loc);
|
||||
buffstart += radix_pos;
|
||||
}
|
||||
}
|
||||
|
||||
o = perform_format_write(buffstart, std::move(o), bufflen);
|
||||
if(trailing_dot){
|
||||
*o++ = radix_char;
|
||||
}
|
||||
return std::move(o);
|
||||
};
|
||||
|
||||
if(should_zero_fill){
|
||||
return outputter(std::move(out));
|
||||
}
|
||||
return perform_format_write_aligned(std::move(out), total_width, specs, alignment::right, outputter);
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(std::monostate, OutIt out, const format_specs&, const std::locale&){
|
||||
return std::move(out);
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt perform_standard_format(std::monostate, OutIt out){
|
||||
return std::move(out);
|
||||
}
|
||||
|
||||
template<class FmtCtx, class ParseCtx, class Specs>
|
||||
template<Handle<FmtCtx> T>
|
||||
constexpr void arg_formatter<FmtCtx,ParseCtx,Specs>::operator()(T&& t){
|
||||
t.format(parse_ctx, fmt_ctx);
|
||||
}
|
||||
template<class FmtCtx, class ParseCtx, class Specs>
|
||||
template<class T>
|
||||
constexpr void arg_formatter<FmtCtx,ParseCtx,Specs>::operator()(T&& t){
|
||||
using handler_t = format_specs_checker<dynamic_format_specs_handler<ParseCtx>>;
|
||||
using specs_t = dynamic_format_specs<typename FmtCtx::char_type>;
|
||||
|
||||
specs_t specs;
|
||||
parse_ctx.advance_to(parse::perform_standard_parse(
|
||||
parse_ctx.begin(),
|
||||
parse_ctx.end(),
|
||||
handler_t{
|
||||
dynamic_format_specs_handler<ParseCtx>{
|
||||
parse_ctx,
|
||||
specs
|
||||
},
|
||||
map_to_storage_enum_v<T,typename FmtCtx::char_type>
|
||||
}
|
||||
));
|
||||
normalize_dynamic_format_specs(fmt_ctx, specs);
|
||||
fmt_ctx.advance_to(perform_standard_format<typename FmtCtx::char_type>(t, fmt_ctx.out(), specs, fmt_ctx.locale()));
|
||||
}
|
||||
template<class FmtCtx, class ParseCtx>
|
||||
template<Handle<FmtCtx> T>
|
||||
constexpr void empty_formatter<FmtCtx,ParseCtx>::operator()(T&& t){
|
||||
t.format(parse_ctx, fmt_ctx);
|
||||
}
|
||||
template<class FmtCtx, class ParseCtx>
|
||||
template<class T>
|
||||
constexpr void empty_formatter<FmtCtx,ParseCtx>::operator()(T&& t){
|
||||
perform_standard_format<typename FmtCtx::char_type>(t, fmt_ctx.out());
|
||||
}
|
||||
|
||||
} //namespace format
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
83
include/rexy/detail/format/internal_types.hpp
Normal file
83
include/rexy/detail/format/internal_types.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_INTERNAL_TYPES_HPP
|
||||
#define REXY_DETAIL_FORMAT_INTERNAL_TYPES_HPP
|
||||
|
||||
#include "../../cx/string.hpp"
|
||||
#include "standard_types.hpp"
|
||||
|
||||
#include <iterator> //back_insert_iterator
|
||||
#include <type_traits> //type_identity
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
//forward declare implementation types
|
||||
template<class Char, class... Args>
|
||||
class basic_format_string;
|
||||
template<class Context, class... Args>
|
||||
class basic_format_arg_store;
|
||||
template<class Char>
|
||||
class format_output_buffer_base;
|
||||
template<class Char, class OutIt>
|
||||
class basic_format_output_buffer;
|
||||
struct format_specs;
|
||||
template<class Char>
|
||||
struct dynamic_format_specs;
|
||||
template<class ParseCtx, class... Args>
|
||||
struct checker_format_specs_handler;
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
struct format_specs_handler;
|
||||
template<class ParseCtx>
|
||||
struct dynamic_format_specs_handler;
|
||||
template<class T, class Char>
|
||||
struct runtime_arg;
|
||||
template<class T, rexy::cx::string Name>
|
||||
struct static_arg;
|
||||
|
||||
//aliases for easier access to both implementation and standard defined types
|
||||
template<class Char>
|
||||
using fmt_buffer_t = format_output_buffer_base<Char>;
|
||||
template<class Char>
|
||||
using output_iterator_t = std::back_insert_iterator<fmt_buffer_t<Char>>;
|
||||
template<class Char>
|
||||
using fmt_context_t = basic_format_context<output_iterator_t<Char>,Char>;
|
||||
template<class Char>
|
||||
using parse_context_t = basic_format_parse_context<Char>;
|
||||
template<class... Args>
|
||||
using format_arg_store = basic_format_arg_store<fmt_context_t<char>, Args...>;
|
||||
template<class... Args>
|
||||
using wformat_arg_store = basic_format_arg_store<fmt_context_t<wchar_t>, Args...>;
|
||||
template<class... Args>
|
||||
using format_string = basic_format_string<char,std::type_identity_t<Args>...>;
|
||||
template<class... Args>
|
||||
using wformat_string = basic_format_string<wchar_t, std::type_identity_t<Args>...>;
|
||||
template<class OutIt>
|
||||
using format_output_buffer = basic_format_output_buffer<char,OutIt>;
|
||||
template<class OutIt>
|
||||
using wformat_output_buffer = basic_format_output_buffer<wchar_t,OutIt>;
|
||||
|
||||
template<class Char>
|
||||
using impl_format_args = basic_format_args<fmt_context_t<Char>>;
|
||||
template<class Char>
|
||||
using format_arg = basic_format_arg<fmt_context_t<Char>>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
117
include/rexy/detail/format/named_args.hpp
Normal file
117
include/rexy/detail/format/named_args.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_NAMED_ARGS_HPP
|
||||
#define REXY_DETAIL_FORMAT_NAMED_ARGS_HPP
|
||||
|
||||
#include <type_traits> //remove_cvref, declval, integral_constant
|
||||
#include <utility> //forward
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
namespace detail{
|
||||
|
||||
template<class T, class Char>
|
||||
struct arg_base{
|
||||
const T& value;
|
||||
const basic_string_view<Char> name;
|
||||
};
|
||||
|
||||
//class used to store name and value for runtime analysis of named arguments
|
||||
//the type T is the actual formatted type and name is used only for lookup
|
||||
//during runtime formatting
|
||||
template<class T, class Char>
|
||||
struct runtime_arg : public arg_base<T,Char>{
|
||||
using char_type = Char;
|
||||
using value_type = std::remove_cvref_t<T>;
|
||||
};
|
||||
|
||||
//class used to store name and value for compile time analysis of named arguments
|
||||
//the type T is the actual formatted type and Name is used for name lookup in
|
||||
//format string checking
|
||||
template<class T, rexy::cx::string Name>
|
||||
struct static_arg : public arg_base<T,typename decltype(Name)::value_type>{
|
||||
using char_type = typename decltype(Name)::value_type;
|
||||
using value_type = std::remove_cvref_t<T>;
|
||||
static constexpr auto static_name = Name;
|
||||
|
||||
constexpr static_arg(const T& v):
|
||||
arg_base<T,char_type>{v, {Name.c_str(), Name.length()}}{}
|
||||
static_arg(const static_arg&) = delete;
|
||||
constexpr static_arg(static_arg&&) = default;
|
||||
};
|
||||
|
||||
//temporary object to store Name to pass off to a compile time argument during assignment
|
||||
template<rexy::cx::string Name>
|
||||
struct arg_literal_result{
|
||||
template<class T>
|
||||
constexpr auto operator=(T&& t){
|
||||
return static_arg<std::remove_cvref_t<T>, Name>{std::forward<T>(t)};
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct is_runtime_named_arg{
|
||||
template<class U, class Char>
|
||||
static auto check(runtime_arg<U,Char>) -> std::true_type;
|
||||
static auto check(...) -> std::false_type;
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>>()))::value;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_runtime_named_arg_v = is_runtime_named_arg<T>::value;
|
||||
|
||||
template<class T>
|
||||
struct is_static_named_arg{
|
||||
template<class U, rexy::cx::string Name>
|
||||
static auto check(static_arg<U,Name>) -> std::true_type;
|
||||
static auto check(...) -> std::false_type;
|
||||
|
||||
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>>()))::value;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_static_named_arg_v = is_static_named_arg<T>::value;
|
||||
|
||||
template<class T>
|
||||
concept RuntimeNamedArg = is_runtime_named_arg_v<T>;
|
||||
template<class T>
|
||||
concept StaticNamedArg = is_static_named_arg_v<T>;
|
||||
|
||||
template<class T>
|
||||
concept NamedArg = StaticNamedArg<T> || RuntimeNamedArg<T>;
|
||||
|
||||
template<class It, std::size_t I, class Arg, class... Args>
|
||||
constexpr std::size_t find_static_named_arg_id_impl(It first, It last);
|
||||
template<class It, class... Args>
|
||||
constexpr std::size_t find_static_named_arg_id(It first, It last);
|
||||
|
||||
template<class... Args>
|
||||
struct count_named_args{
|
||||
static constexpr std::size_t value = (std::size_t{NamedArg<Args>} + ...);
|
||||
};
|
||||
template<class... Args>
|
||||
static constexpr std::size_t count_named_args_v = count_named_args<Args...>::value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
57
include/rexy/detail/format/named_args.tpp
Normal file
57
include/rexy/detail/format/named_args.tpp
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_NAMED_ARGS_TPP
|
||||
#define REXY_DETAIL_FORMAT_NAMED_ARGS_TPP
|
||||
|
||||
#include "named_args.hpp"
|
||||
|
||||
#include "basic_types.hpp"
|
||||
#include "../../utility.hpp" //strcmp
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
template<class It, std::size_t I, class Arg, class... Args>
|
||||
constexpr std::size_t find_static_named_arg_id_impl(It first, It last){
|
||||
if constexpr(StaticNamedArg<Arg>){
|
||||
const auto len = last - first;
|
||||
const auto arg_len = Arg::static_name.length();
|
||||
if(len == arg_len){
|
||||
if(!rexy::strncmp(first, Arg::static_name.c_str(), arg_len)){
|
||||
return I;
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr(sizeof...(Args) > 0){
|
||||
return find_static_named_arg_id_impl<It,I+1,Args...>(first, last);
|
||||
}else{
|
||||
return invalid_arg_index;
|
||||
}
|
||||
}
|
||||
template<class It, class... Args>
|
||||
constexpr std::size_t find_static_named_arg_id(It first, It last){
|
||||
if constexpr(sizeof...(Args) == 0){
|
||||
return invalid_arg_index;
|
||||
}else{
|
||||
return find_static_named_arg_id_impl<It, 0, Args...>(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
123
include/rexy/detail/format/output_buffer.hpp
Normal file
123
include/rexy/detail/format/output_buffer.hpp
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_OUTPUT_BUFFER_HPP
|
||||
#define REXY_DETAIL_FORMAT_OUTPUT_BUFFER_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include <cstdio> //fputc, FILE
|
||||
#include <cwchar> //fputwc
|
||||
|
||||
#include "format_error.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
//polymorphic buffer of which a reference can be passed around to prevent
|
||||
//passing around multiple output iterator types
|
||||
template<class Char>
|
||||
class format_output_buffer_base
|
||||
{
|
||||
public:
|
||||
using value_type = Char;
|
||||
protected:
|
||||
static constexpr std::size_t s_bufsize = 256;
|
||||
protected:
|
||||
Char m_data[s_bufsize];
|
||||
std::size_t m_size = 0;
|
||||
std::size_t m_written_count = 0;
|
||||
public:
|
||||
format_output_buffer_base(void) = default;
|
||||
virtual ~format_output_buffer_base(void) = default;
|
||||
|
||||
virtual std::size_t write_out(void) = 0;
|
||||
void clear(void);
|
||||
void push_back(Char c);
|
||||
|
||||
constexpr std::size_t count(void)const;
|
||||
};
|
||||
|
||||
//Used with the 'formatter' to output a type
|
||||
//OutIt is an iterator where the output is written at the end.
|
||||
template<class Char, class OutIt>
|
||||
class basic_format_output_buffer : public format_output_buffer_base<Char>
|
||||
{
|
||||
private:
|
||||
using base = format_output_buffer_base<Char>;
|
||||
private:
|
||||
OutIt m_out;
|
||||
public:
|
||||
basic_format_output_buffer(OutIt out);
|
||||
~basic_format_output_buffer(void)override;
|
||||
|
||||
std::size_t write_out(void)override;
|
||||
|
||||
constexpr OutIt out(void);
|
||||
private:
|
||||
std::size_t write_out_simple(void);
|
||||
};
|
||||
template<class Char, class OutIt>
|
||||
class basic_format_output_n_buffer : public format_output_buffer_base<Char>
|
||||
{
|
||||
private:
|
||||
using base = format_output_buffer_base<Char>;
|
||||
private:
|
||||
OutIt m_out;
|
||||
std::size_t m_max_write = 0;
|
||||
public:
|
||||
basic_format_output_n_buffer(OutIt out, std::size_t max);
|
||||
~basic_format_output_n_buffer(void)override;
|
||||
|
||||
std::size_t write_out(void)override;
|
||||
|
||||
constexpr OutIt out(void);
|
||||
};
|
||||
|
||||
template<class Char>
|
||||
class basic_format_size_buffer : public format_output_buffer_base<Char>
|
||||
{
|
||||
public:
|
||||
constexpr basic_format_size_buffer(void) = default;
|
||||
constexpr ~basic_format_size_buffer(void)override;
|
||||
|
||||
std::size_t write_out(void)override;
|
||||
};
|
||||
|
||||
|
||||
template<class Char>
|
||||
struct print_format_buffer : public format_output_buffer_base<Char>
|
||||
{
|
||||
private:
|
||||
FILE* m_stream;
|
||||
public:
|
||||
constexpr print_format_buffer(FILE* stream):
|
||||
m_stream(stream){}
|
||||
constexpr ~print_format_buffer(void){
|
||||
write_out();
|
||||
}
|
||||
|
||||
std::size_t write_out(void)override{
|
||||
const auto written = fwrite(this->m_data, sizeof(this->m_data[0]), this->m_size, m_stream);
|
||||
if(written != this->m_size){
|
||||
REXY_THROW_FORMAT_ERROR("Failed to print data");
|
||||
}
|
||||
return written;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
153
include/rexy/detail/format/output_buffer.tpp
Normal file
153
include/rexy/detail/format/output_buffer.tpp
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_OUTPUT_BUFFER_TPP
|
||||
#define REXY_DETAIL_FORMAT_OUTPUT_BUFFER_TPP
|
||||
|
||||
#include "output_buffer.hpp"
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include <utility> //move
|
||||
#include <type_traits> //is_pointer, remove_cvref, integral_constant
|
||||
#include <algorithm> //min
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
///////////////////////format_output_buffer_base////////////////////////
|
||||
template<class Char>
|
||||
void format_output_buffer_base<Char>::clear(void){
|
||||
m_size = 0;
|
||||
}
|
||||
template<class Char>
|
||||
void format_output_buffer_base<Char>::push_back(Char c){
|
||||
if(m_size == s_bufsize){
|
||||
m_written_count += write_out();
|
||||
clear();
|
||||
}
|
||||
m_data[m_size++] = c;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr std::size_t format_output_buffer_base<Char>::count(void)const{
|
||||
//include data that will be written during next call to write_out
|
||||
return m_written_count + m_size;
|
||||
}
|
||||
|
||||
///////////////////////basic_format_output_buffer////////////////////////
|
||||
template<class Char, class OutIt>
|
||||
basic_format_output_buffer<Char,OutIt>::basic_format_output_buffer(OutIt out):
|
||||
m_out(out){}
|
||||
template<class Char, class OutIt>
|
||||
basic_format_output_buffer<Char,OutIt>::~basic_format_output_buffer(void){
|
||||
write_out();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
concept HasContainerAlias = requires{
|
||||
typename T::container_type;
|
||||
};
|
||||
|
||||
template<class T, class It>
|
||||
concept HasInsertMethod = requires(T t, It i){
|
||||
{t.insert(1, i, i)}; //don't use 0 index because pointer conversion causes ambiguous call
|
||||
{t.size()};
|
||||
};
|
||||
|
||||
template<class It>
|
||||
struct something : public It{
|
||||
template<class U = something, class C = decltype(U::container)>
|
||||
static std::true_type check(int);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = decltype(check(0))::value;
|
||||
};
|
||||
|
||||
template<class Char, class OutIt>
|
||||
OutIt real_write_out(const Char* start, std::size_t write_count, OutIt out){
|
||||
static constexpr bool has_container = something<OutIt>::value;//decltype(container_traits<OutIt>::check(0))::value;
|
||||
|
||||
//optimize for types where direct access to the underlying container is available
|
||||
//check for a container member and a 'container_type' typedef in OutIt
|
||||
//reason for the type check is that without 'container_type' it might not be safe to access the 'container'
|
||||
if constexpr(has_container && HasContainerAlias<OutIt>){
|
||||
struct container_access : public OutIt{
|
||||
using OutIt::container;
|
||||
};
|
||||
container_access ca{out};
|
||||
auto& container = ca.container;
|
||||
|
||||
if constexpr(HasInsertMethod<typename OutIt::container_type,const Char*>){
|
||||
if constexpr(std::is_pointer_v<std::remove_cvref_t<decltype(container)>>){
|
||||
container->insert(container->size(), start, start + write_count);
|
||||
}else{
|
||||
container.insert(container.size(), start, start + write_count);
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
}
|
||||
for(std::size_t i = 0;i < write_count;++i){
|
||||
out = start[i];
|
||||
}
|
||||
return std::move(out);
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
std::size_t basic_format_output_buffer<Char,OutIt>::write_out(void){
|
||||
m_out = real_write_out(this->m_data, this->m_size, std::move(m_out));
|
||||
return this->m_size;
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt basic_format_output_buffer<Char,OutIt>::out(void){
|
||||
return std::move(m_out);
|
||||
}
|
||||
|
||||
///////////////////////basic_format_output_n_buffer////////////////////////
|
||||
template<class Char, class OutIt>
|
||||
basic_format_output_n_buffer<Char,OutIt>::basic_format_output_n_buffer(OutIt out, std::size_t max):
|
||||
m_out(out),
|
||||
m_max_write(max){}
|
||||
template<class Char, class OutIt>
|
||||
basic_format_output_n_buffer<Char,OutIt>::~basic_format_output_n_buffer(void){
|
||||
write_out();
|
||||
}
|
||||
|
||||
template<class Char, class OutIt>
|
||||
std::size_t basic_format_output_n_buffer<Char,OutIt>::write_out(void){
|
||||
const auto to_write = std::min(this->size, m_max_write);
|
||||
|
||||
m_out = real_write_out(this->m_data, to_write, std::move(m_out));
|
||||
m_max_write -= to_write;
|
||||
return to_write;
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
constexpr OutIt basic_format_output_n_buffer<Char,OutIt>::out(void){
|
||||
return std::move(m_out);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////basic_format_size_buffer//////////////////////////
|
||||
template<class Char>
|
||||
constexpr basic_format_size_buffer<Char>::~basic_format_size_buffer(void){
|
||||
write_out();
|
||||
}
|
||||
template<class Char>
|
||||
std::size_t basic_format_size_buffer<Char>::write_out(void){
|
||||
return this->m_size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
116
include/rexy/detail/format/parse.hpp
Normal file
116
include/rexy/detail/format/parse.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_PARSE_HPP
|
||||
#define REXY_DETAIL_FORMAT_PARSE_HPP
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy::fmt::detail::parse{
|
||||
|
||||
//A few basic string checking functions
|
||||
template<class Char>
|
||||
constexpr auto find_first_of_it(Char v,
|
||||
typename basic_string_view<Char>::const_iterator start_it,
|
||||
typename basic_string_view<Char>::const_iterator last_it);
|
||||
template<class Char>
|
||||
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v);
|
||||
template<class It>
|
||||
constexpr bool check_duplicate_char(It start, It end);
|
||||
template<class Char>
|
||||
constexpr bool is_a_number(Char c);
|
||||
template<class It>
|
||||
constexpr int to_integer(It start, It end);
|
||||
template<class It>
|
||||
constexpr It find_valid_index(It start, It end);
|
||||
|
||||
//Used with 'visit_format_arg' as the 'fun' arg. Gets the value of the argument as a 'long long'
|
||||
//if the argument is of integral type. Throws an error otherwise
|
||||
struct dynamic_integer_retriever{
|
||||
template<class T>
|
||||
constexpr long long operator()(T&& t);
|
||||
};
|
||||
|
||||
//Used during call to 'perform_standard_nested_replacement_field' as the 'Adapter' type.
|
||||
//Calls 'on_dynamic_width' of 'specs'
|
||||
template<class Specs>
|
||||
struct dynamic_width_adapter{
|
||||
Specs& specs;
|
||||
|
||||
constexpr decltype(auto) operator()(void);
|
||||
constexpr decltype(auto) operator()(int i);
|
||||
template<class It>
|
||||
constexpr decltype(auto) operator()(It start, It last);
|
||||
};
|
||||
//Used during call to 'perform_standard_nested_replacement_field' as the 'Adapter' type.
|
||||
//Calls 'on_dynamic_precision' of 'specs'
|
||||
template<class Specs>
|
||||
struct dynamic_precision_adapter{
|
||||
Specs& specs;
|
||||
|
||||
constexpr decltype(auto) operator()(void);
|
||||
constexpr decltype(auto) operator()(int i);
|
||||
template<class It>
|
||||
constexpr decltype(auto) operator()(It start, It last);
|
||||
};
|
||||
template<class Handler>
|
||||
struct dynamic_index_adapter{
|
||||
Handler& handler;
|
||||
std::size_t& id;
|
||||
constexpr decltype(auto) operator()(void);
|
||||
constexpr decltype(auto) operator()(int i);
|
||||
template<class It>
|
||||
constexpr decltype(auto) operator()(It start, It last);
|
||||
};
|
||||
|
||||
//Standalone functions for parsing standard format fields
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class Adapter>
|
||||
constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs);
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs);
|
||||
|
||||
//Standalone functions for parsing the entire format string
|
||||
template<class Handler, class It>
|
||||
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id);
|
||||
template<class Handler, class It>
|
||||
constexpr It perform_empty_format_field_parse(Handler&& handler, It start, It last);
|
||||
template<class Handler, class It>
|
||||
constexpr It perform_format_field_parse(Handler&& handler, It start, It last);
|
||||
template<class Handler, class Char>
|
||||
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> fmt);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
354
include/rexy/detail/format/parse.tpp
Normal file
354
include/rexy/detail/format/parse.tpp
Normal file
@ -0,0 +1,354 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_PARSE_TPP
|
||||
#define REXY_DETAIL_FORMAT_PARSE_TPP
|
||||
|
||||
#include "parse.hpp"
|
||||
|
||||
#include "format_error.hpp"
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include <type_traits> //is_integral, remove_cvref, add_lvalue_reference, remove_reference
|
||||
#include <algorithm> //min
|
||||
|
||||
namespace rexy::fmt::detail::parse{
|
||||
|
||||
template<class Char>
|
||||
constexpr auto find_first_of_it(Char v,
|
||||
typename basic_string_view<Char>::const_iterator start_it,
|
||||
typename basic_string_view<Char>::const_iterator last_it)
|
||||
{
|
||||
const basic_string_view<Char> view{start_it, last_it};
|
||||
const std::size_t range_size = last_it - start_it;
|
||||
return start_it + std::min(view.find_first_of(v), range_size);
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v){
|
||||
return find_first_of_it(v, str.begin(), str.end());
|
||||
}
|
||||
template<class It>
|
||||
constexpr bool check_duplicate_char(It start, It end){
|
||||
if(start == end){
|
||||
return false;
|
||||
}
|
||||
const auto next = start + 1;
|
||||
return *start == *next;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool is_a_number(Char c){
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool is_valid_name_char(Char c){
|
||||
return (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c == '_');
|
||||
}
|
||||
template<class It>
|
||||
constexpr int to_integer(It start, It end){
|
||||
int retval = 0;
|
||||
std::size_t mul = 1;
|
||||
for(auto it = end;it != start;--it){
|
||||
const auto cur = it-1;
|
||||
retval += ((*cur - '0') * mul);
|
||||
mul *= 10;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr long long dynamic_integer_retriever::operator()(T&& t){
|
||||
if constexpr(std::is_integral_v<std::remove_cvref_t<T>>){
|
||||
return static_cast<long long>(t);
|
||||
}else{
|
||||
REXY_THROW_FORMAT_ERROR("Invalid dynamic specifier");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
template<class Specs>
|
||||
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(void){
|
||||
return specs.on_dynamic_width();
|
||||
}
|
||||
template<class Specs>
|
||||
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(int i){
|
||||
return specs.on_dynamic_width(i);
|
||||
}
|
||||
template<class Specs>
|
||||
template<class It>
|
||||
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(It start, It last){
|
||||
return specs.on_dynamic_width(start, last);
|
||||
}
|
||||
template<class Specs>
|
||||
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(void){
|
||||
return specs.on_dynamic_precision();
|
||||
}
|
||||
template<class Specs>
|
||||
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(int i){
|
||||
return specs.on_dynamic_precision(i);
|
||||
}
|
||||
template<class Specs>
|
||||
template<class It>
|
||||
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(It start, It last){
|
||||
return specs.on_dynamic_precision(start, last);
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(void){
|
||||
return (id = handler.on_arg_id());
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(int i){
|
||||
id = i;
|
||||
return handler.on_arg_id(i);
|
||||
}
|
||||
template<class Handler>
|
||||
template<class It>
|
||||
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(It start, It last){
|
||||
id = handler.on_arg_id(start, last);
|
||||
return id;
|
||||
}
|
||||
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs){
|
||||
char fill_val = ' ';
|
||||
It it = start;
|
||||
for(int i = 0;i < 2 && it != last;++i){
|
||||
if(*it == '>' || *it == '<' || *it == '^'){
|
||||
specs.on_align(*it, fill_val);
|
||||
return ++it;
|
||||
}
|
||||
fill_val = *it;
|
||||
++it;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs){
|
||||
if(*start == '+' || *start == '-' || *start == ' '){
|
||||
specs.on_sign(*start);
|
||||
return ++start;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs){
|
||||
if(*start == '#'){
|
||||
specs.on_alt_form();
|
||||
++start;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs){
|
||||
if(*start == '0'){
|
||||
specs.on_zero_fill();
|
||||
++start;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class It, class Adapter>
|
||||
constexpr It perform_index_retrieve(It start, It last, Adapter&& func){
|
||||
if(*start == '0'){
|
||||
func(0);
|
||||
return start+1;
|
||||
}
|
||||
if(*start == '}' || *start == ':'){
|
||||
func();
|
||||
return start;
|
||||
}
|
||||
if(!is_a_number(*start)){
|
||||
auto it = start;
|
||||
for(;it != last;++it){
|
||||
if(!is_valid_name_char(*it)){
|
||||
func(start, it);
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
for(auto it = start+1;it != last;++it){
|
||||
if(!is_a_number(*it)){
|
||||
func(to_integer(start, it));
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
template<class It, class Adapter>
|
||||
constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func){
|
||||
auto it = perform_index_retrieve(start, last, func);
|
||||
if(*it != '}'){
|
||||
REXY_THROW_FORMAT_ERROR("Dynamic index invalid spec");
|
||||
}
|
||||
return it+1;
|
||||
}
|
||||
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs){
|
||||
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
|
||||
if(*start == '{'){
|
||||
return perform_standard_nested_replacement_field(start+1, last, dynamic_width_adapter<specs_t>{specs});
|
||||
}
|
||||
if(*start == '0'){
|
||||
REXY_THROW_FORMAT_ERROR("Field invalid spec");
|
||||
}
|
||||
It it = start;
|
||||
for(;is_a_number(*it) && it != last;++it){}
|
||||
if(it == start){
|
||||
return start;
|
||||
}
|
||||
specs.on_width(to_integer(start, it));
|
||||
return it;
|
||||
}
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs){
|
||||
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
|
||||
if(*start != '.'){
|
||||
return start;
|
||||
}
|
||||
++start;
|
||||
if(*start == '{'){
|
||||
return perform_standard_nested_replacement_field(start+1, last, dynamic_precision_adapter<specs_t>{specs});
|
||||
}
|
||||
if(*start == '0'){
|
||||
REXY_THROW_FORMAT_ERROR("Field invalid spec");
|
||||
}
|
||||
It it = start;
|
||||
for(;is_a_number(*it) && it != last;++it){}
|
||||
if(it == start){
|
||||
REXY_THROW_FORMAT_ERROR("Field invalid spec");
|
||||
}
|
||||
specs.on_precision(to_integer(start, it));
|
||||
return it;
|
||||
}
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs){
|
||||
if(*start == 'L'){
|
||||
specs.on_locale();
|
||||
return ++start;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs){
|
||||
if(*start == '}'){
|
||||
specs.on_type_option();
|
||||
return start;
|
||||
}
|
||||
specs.on_type_option(*start);
|
||||
return ++start;
|
||||
}
|
||||
|
||||
template<class It, class FormatSpecs>
|
||||
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs){
|
||||
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
|
||||
using std_fmt_fn = It(*)(It,It,specs_t);
|
||||
constexpr std_fmt_fn fns[] = {
|
||||
perform_standard_fill_align_option<It,specs_t>,
|
||||
perform_standard_sign_option<It,specs_t>,
|
||||
perform_standard_alt_form_option<It,specs_t>,
|
||||
perform_standard_zero_fill_option<It,specs_t>,
|
||||
perform_standard_width_option<It,specs_t>,
|
||||
perform_standard_precision_option<It,specs_t>,
|
||||
perform_standard_locale_option<It,specs_t>,
|
||||
perform_standard_type_option<It,specs_t>
|
||||
};
|
||||
constexpr int num_std_fns = sizeof(fns) / sizeof(fns[0]);
|
||||
|
||||
for(int i = 0;i < num_std_fns;++i){
|
||||
start = fns[i](start, last, specs);
|
||||
if(start == last){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(*start != '}'){
|
||||
REXY_THROW_FORMAT_ERROR("Missing closing brace for format field");
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
template<class Handler, class It>
|
||||
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id){
|
||||
dynamic_index_adapter<Handler> adapter{handler, id};
|
||||
const auto it = perform_index_retrieve(start, last, adapter);
|
||||
if(*it != '}' && *it != ':'){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid index spec");
|
||||
}
|
||||
return it;
|
||||
}
|
||||
template<class Handler, class It>
|
||||
constexpr It perform_format_field_parse(Handler&& handler, It start, It last){
|
||||
if(start == last){
|
||||
REXY_THROW_FORMAT_ERROR("Unmatched brace");
|
||||
}
|
||||
std::size_t index = 0;
|
||||
start = perform_format_index_spec(handler, start, last, index);
|
||||
if(*start == ':'){
|
||||
return handler.do_format_spec(start+1, last, index);
|
||||
}
|
||||
handler.do_empty_format(start, last, index);
|
||||
return start;
|
||||
}
|
||||
template<class Handler, class Char>
|
||||
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> fmt){
|
||||
using char_type = Char;
|
||||
|
||||
auto work_it = fmt.begin();
|
||||
auto open_brace_it = fmt.begin();
|
||||
auto close_brace_it = fmt.begin();
|
||||
|
||||
while(true){
|
||||
open_brace_it = find_first_of_it(char_type{'{'}, work_it, fmt.end());
|
||||
|
||||
while(true){
|
||||
close_brace_it = find_first_of_it(char_type{'}'}, work_it, open_brace_it);
|
||||
if(close_brace_it == open_brace_it){
|
||||
//don't go past open brace because we still need to parse it
|
||||
handler.do_raw(work_it, close_brace_it);
|
||||
work_it = close_brace_it;
|
||||
break;
|
||||
}
|
||||
if(check_duplicate_char(close_brace_it, fmt.end())){
|
||||
++close_brace_it;
|
||||
handler.do_raw(work_it, close_brace_it);
|
||||
//go past closing brace to not double parse it
|
||||
work_it = close_brace_it + 1;
|
||||
}else{
|
||||
REXY_THROW_FORMAT_ERROR("Unmatched brace");
|
||||
}
|
||||
}
|
||||
if(open_brace_it == fmt.end()){
|
||||
break;
|
||||
}
|
||||
|
||||
if(check_duplicate_char(open_brace_it, fmt.end())){
|
||||
++open_brace_it;
|
||||
handler.do_raw(work_it, open_brace_it);
|
||||
work_it = open_brace_it + 1;
|
||||
continue;
|
||||
}
|
||||
work_it = perform_format_field_parse(handler, open_brace_it+1, fmt.end()) + 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
61
include/rexy/detail/format/parse_context.hpp
Normal file
61
include/rexy/detail/format/parse_context.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_PARSE_CONTEXT_HPP
|
||||
#define REXY_DETAIL_FORMAT_PARSE_CONTEXT_HPP
|
||||
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include "../../string_view.hpp"
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
//Holds the format string for use in calls to 'formatter<T>::parse'
|
||||
//Additionally keeps tally of the current argument index for automatic replacement indexing or
|
||||
//switches to manual indexing mode when encountering a manually indexed field. Throws error on mode switch.
|
||||
template<class Char>
|
||||
class basic_format_parse_context
|
||||
{
|
||||
public:
|
||||
using char_type = Char;
|
||||
using iterator = typename basic_string_view<Char>::const_iterator;
|
||||
using const_iterator = typename basic_string_view<Char>::const_iterator;
|
||||
|
||||
public:
|
||||
basic_string_view<char_type> format;
|
||||
std::size_t arg_count;
|
||||
long long arg_index = 0;
|
||||
|
||||
public:
|
||||
//fmt is the format specifier, num_args is 'sizeof...(Args)' where 'Args' is the parameter pack of the format call
|
||||
constexpr explicit basic_format_parse_context(basic_string_view<char_type> fmt, std::size_t num_args = 0)noexcept;
|
||||
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
||||
basic_format_parse_context(basic_format_parse_context&&) = delete;
|
||||
basic_format_parse_context& operator=(basic_format_parse_context&&) = delete;
|
||||
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
|
||||
|
||||
constexpr const_iterator begin(void)const noexcept;
|
||||
constexpr const_iterator end(void)const noexcept;
|
||||
constexpr void advance_to(const_iterator it);
|
||||
constexpr std::size_t next_arg_id(void);
|
||||
constexpr void check_arg_id(std::size_t id);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
80
include/rexy/detail/format/parse_context.tpp
Normal file
80
include/rexy/detail/format/parse_context.tpp
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_PARSE_CONTEXT_TPP
|
||||
#define REXY_DETAIL_FORMAT_PARSE_CONTEXT_TPP
|
||||
|
||||
#include "../../compat/if_consteval.hpp"
|
||||
|
||||
#include "parse_context.hpp"
|
||||
|
||||
#include "format_error.hpp"
|
||||
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
/////////////////////////////basic_format_parse_context//////////////////////////////
|
||||
template<class Char>
|
||||
constexpr basic_format_parse_context<Char>::basic_format_parse_context(basic_string_view<char_type> fmt, std::size_t num_args)noexcept:
|
||||
format(fmt),
|
||||
arg_count(num_args){}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto basic_format_parse_context<Char>::begin(void)const noexcept -> const_iterator{
|
||||
return format.begin();
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_format_parse_context<Char>::end(void)const noexcept -> const_iterator{
|
||||
return format.end();
|
||||
}
|
||||
template<class Char>
|
||||
constexpr void basic_format_parse_context<Char>::advance_to(const_iterator it){
|
||||
const std::size_t diff = it - format.begin();
|
||||
format.remove_prefix(diff);
|
||||
}
|
||||
template<class Char>
|
||||
constexpr std::size_t basic_format_parse_context<Char>::next_arg_id(void){
|
||||
if(arg_index < 0){
|
||||
//error, already in manual mode
|
||||
REXY_THROW_FORMAT_ERROR("Invalid indexing mode switch");
|
||||
}
|
||||
REXY_if_consteval{
|
||||
if(arg_index >= arg_count){
|
||||
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||
}
|
||||
}
|
||||
return arg_index++;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr void basic_format_parse_context<Char>::check_arg_id(std::size_t id){
|
||||
if(arg_index > 0){
|
||||
//error, already in automatic mode
|
||||
REXY_THROW_FORMAT_ERROR("Invalid indexing mode switch");
|
||||
}
|
||||
REXY_if_consteval{
|
||||
if(id >= arg_count){
|
||||
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||
}
|
||||
}
|
||||
arg_index = detail::invalid_arg_index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
129
include/rexy/detail/format/specs_handler.hpp
Normal file
129
include/rexy/detail/format/specs_handler.hpp
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_SPECS_HANDLER_HPP
|
||||
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_HPP
|
||||
|
||||
#include "../../compat/if_consteval.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
//Stores standard format-spec information to use later in formatting
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
struct format_specs_handler{
|
||||
ParseCtx& parse_ctx;
|
||||
FmtCtx& fmt_ctx;
|
||||
format_specs& specs;
|
||||
|
||||
constexpr format_specs_handler(ParseCtx& pc, FmtCtx& fc, format_specs& s):
|
||||
parse_ctx(pc),
|
||||
fmt_ctx(fc),
|
||||
specs(s){}
|
||||
|
||||
constexpr void on_width(int w);
|
||||
constexpr void on_dynamic_width(void);
|
||||
constexpr void on_dynamic_width(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_width(It first, It last);
|
||||
constexpr void on_precision(int p);
|
||||
constexpr void on_dynamic_precision(void);
|
||||
constexpr void on_dynamic_precision(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_precision(It first, It last);
|
||||
constexpr void on_locale(void);
|
||||
constexpr void on_type_option(int o = 0);
|
||||
constexpr void on_align(int al, int val);
|
||||
constexpr void on_sign(int s);
|
||||
constexpr void on_alt_form(void);
|
||||
constexpr void on_zero_fill(void);
|
||||
};
|
||||
|
||||
//Stores standard format-spec information to use later in formatting. Stores indices of dynamic specifiers
|
||||
//to be retrieved later.
|
||||
template<class ParseCtx>
|
||||
struct dynamic_format_specs_handler{
|
||||
using specs_type = dynamic_format_specs<typename ParseCtx::char_type>;
|
||||
ParseCtx& parse_ctx;
|
||||
specs_type& specs;
|
||||
|
||||
constexpr dynamic_format_specs_handler(ParseCtx& pc, specs_type& s);
|
||||
|
||||
constexpr void on_width(int w);
|
||||
constexpr void on_dynamic_width(void);
|
||||
constexpr void on_dynamic_width(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_width(It first, It last);
|
||||
constexpr void on_precision(int p);
|
||||
constexpr void on_dynamic_precision(void);
|
||||
constexpr void on_dynamic_precision(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_precision(It first, It last);
|
||||
constexpr void on_locale(void);
|
||||
constexpr void on_type_option(int o = 0);
|
||||
constexpr void on_align(int al, int val);
|
||||
constexpr void on_sign(int s);
|
||||
constexpr void on_alt_form(void);
|
||||
constexpr void on_zero_fill(void);
|
||||
};
|
||||
//Used during compile time only. Checks dynamic specifiers given types pack as template parameters.
|
||||
template<class ParseCtx, class... Args>
|
||||
struct cx_format_specs_handler : public dynamic_format_specs_handler<ParseCtx>{
|
||||
using char_type = typename ParseCtx::char_type;
|
||||
|
||||
constexpr cx_format_specs_handler(ParseCtx& pc, dynamic_format_specs<char_type>& s):
|
||||
dynamic_format_specs_handler<ParseCtx>(pc, s){
|
||||
REXY_if_not_consteval{
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void on_dynamic_width(void);
|
||||
constexpr void on_dynamic_width(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_width(It first, It last);
|
||||
constexpr void on_dynamic_precision(void);
|
||||
constexpr void on_dynamic_precision(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_precision(It first, It last);
|
||||
};
|
||||
|
||||
template<class Handler>
|
||||
struct format_specs_checker : public Handler{
|
||||
storage_type arg_type = storage_type::none_t;
|
||||
bool requires_arithmetic_presentation = false;
|
||||
|
||||
constexpr format_specs_checker(const Handler& h, storage_type type);
|
||||
|
||||
constexpr void on_precision(int p);
|
||||
constexpr void on_dynamic_precision(void);
|
||||
constexpr void on_dynamic_precision(int index);
|
||||
template<class It>
|
||||
constexpr void on_dynamic_precision(It first, It last);
|
||||
constexpr void on_locale(void);
|
||||
constexpr void on_sign(int s);
|
||||
constexpr void on_alt_form(void);
|
||||
constexpr void on_zero_fill(void);
|
||||
constexpr void on_type_option(int type = 0);
|
||||
private:
|
||||
constexpr void check_precision(void);
|
||||
constexpr void check_arithmetic_type(void);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
389
include/rexy/detail/format/specs_handler.tpp
Normal file
389
include/rexy/detail/format/specs_handler.tpp
Normal file
@ -0,0 +1,389 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_SPECS_HANDLER_TPP
|
||||
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_TPP
|
||||
|
||||
#include "specs_handler.hpp"
|
||||
|
||||
#include "format_error.hpp"
|
||||
#include "traits.hpp"
|
||||
#include "named_args.hpp"
|
||||
#include "format_args.hpp"
|
||||
|
||||
#include "internal_types.hpp"
|
||||
#include "standard_types.hpp"
|
||||
#include "basic_types.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
/////////////////////////////format_specs_handler//////////////////////////////
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_width(int w){
|
||||
specs.width = w;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(void){
|
||||
const auto index = parse_ctx.next_arg_id();
|
||||
const auto arg = fmt_ctx.arg(index);
|
||||
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||
on_width(value);
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(int index){
|
||||
parse_ctx.check_arg_id(index);
|
||||
const auto arg = fmt_ctx.arg(index);
|
||||
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||
on_width(value);
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
template<class It>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(It first, It last){
|
||||
on_dynamic_width(fmt_ctx.arg_index(first, last));
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_precision(int p){
|
||||
specs.precision = p;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(void){
|
||||
const auto index = parse_ctx.next_arg_id();
|
||||
const auto arg = fmt_ctx.arg(index);
|
||||
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||
on_precision(value);
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(int index){
|
||||
parse_ctx.check_arg_id(index);
|
||||
const auto arg = fmt_ctx.arg(index);
|
||||
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||
on_precision(value);
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
template<class It>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(It first, It last){
|
||||
on_dynamic_precision(fmt_ctx.arg_index(first, last));
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_locale(void){
|
||||
specs.locale = true;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_type_option(int o){
|
||||
specs.type = o;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_align(int al, int val){
|
||||
specs.align = static_cast<alignment>(al);
|
||||
specs.align_char = val;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_sign(int s){
|
||||
specs.sign = s;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_alt_form(void){
|
||||
specs.alt_form = true;
|
||||
}
|
||||
template<class ParseCtx, class FmtCtx>
|
||||
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_zero_fill(void){
|
||||
specs.zero_fill = true;
|
||||
}
|
||||
|
||||
/////////////////////////////dynamic_format_specs_handler//////////////////////////////
|
||||
template<class ParseCtx>
|
||||
constexpr dynamic_format_specs_handler<ParseCtx>::dynamic_format_specs_handler(ParseCtx& pc, dynamic_format_specs<typename ParseCtx::char_type>& s):
|
||||
parse_ctx(pc),
|
||||
specs(s){}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_width(int w){
|
||||
specs.width = w;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(void){
|
||||
specs.dyn_width.index = parse_ctx.next_arg_id();
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(int index){
|
||||
parse_ctx.check_arg_id(index);
|
||||
specs.dyn_width.index = index;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
template<class It>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(It first, It last){
|
||||
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
|
||||
specs.dyn_width.name = {first, std::size_t(last - first)};
|
||||
specs.width_type = dyn_type::string;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_precision(int p){
|
||||
specs.precision = p;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(void){
|
||||
specs.dyn_precision.index = parse_ctx.next_arg_id();
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(int index){
|
||||
parse_ctx.check_arg_id(index);
|
||||
specs.dyn_precision.index = index;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
template<class It>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(It first, It last){
|
||||
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
|
||||
specs.dyn_precision.name = {first, std::size_t(last - first)};
|
||||
specs.precision_type = dyn_type::string;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_locale(void){
|
||||
specs.locale = true;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_type_option(int o){
|
||||
specs.type = o;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_align(int al, int val){
|
||||
specs.align = static_cast<alignment>(al);
|
||||
specs.align_char = val;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_sign(int s){
|
||||
specs.sign = s;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_alt_form(void){
|
||||
specs.alt_form = true;
|
||||
}
|
||||
template<class ParseCtx>
|
||||
constexpr void dynamic_format_specs_handler<ParseCtx>::on_zero_fill(void){
|
||||
specs.zero_fill = true;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////cx_format_specs_handler//////////////////////////////
|
||||
template<class ParseCtx, class... Args>
|
||||
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(void){
|
||||
const auto index = this->parse_ctx.next_arg_id();
|
||||
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
|
||||
}
|
||||
}
|
||||
template<class ParseCtx, class... Args>
|
||||
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(int index){
|
||||
this->parse_ctx.check_arg_id(index);
|
||||
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
|
||||
}
|
||||
}
|
||||
template<class ParseCtx, class... Args>
|
||||
template<class It>
|
||||
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(It first, It last){
|
||||
on_dynamic_width(find_static_named_arg_id<It,Args...>(first, last));
|
||||
}
|
||||
template<class ParseCtx, class... Args>
|
||||
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(void){
|
||||
const auto index = this->parse_ctx.next_arg_id();
|
||||
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
|
||||
}
|
||||
}
|
||||
template<class ParseCtx, class... Args>
|
||||
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(int index){
|
||||
this->parse_ctx.check_arg_id(index);
|
||||
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
|
||||
}
|
||||
}
|
||||
template<class ParseCtx, class... Args>
|
||||
template<class It>
|
||||
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(It first, It last){
|
||||
on_dynamic_precision(find_static_named_arg_id<It,Args...>(first, last));
|
||||
}
|
||||
|
||||
/////////////////////////////format_specs_checker//////////////////////////////
|
||||
template<class Handler>
|
||||
constexpr format_specs_checker<Handler>::format_specs_checker(const Handler& h, storage_type type):
|
||||
Handler(h), arg_type(type){}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_precision(int p){
|
||||
check_precision();
|
||||
Handler::on_precision(p);
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_dynamic_precision(void){
|
||||
check_precision();
|
||||
Handler::on_dynamic_precision();
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_dynamic_precision(int index){
|
||||
check_precision();
|
||||
Handler::on_dynamic_precision(index);
|
||||
}
|
||||
template<class Handler>
|
||||
template<class It>
|
||||
constexpr void format_specs_checker<Handler>::on_dynamic_precision(It first, It last){
|
||||
check_precision();
|
||||
Handler::on_dynamic_precision(first, last);
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_locale(void){
|
||||
check_arithmetic_type();
|
||||
Handler::on_locale();
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_sign(int s){
|
||||
requires_arithmetic_presentation = true;
|
||||
check_arithmetic_type();
|
||||
Handler::on_sign(s);
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_alt_form(void){
|
||||
requires_arithmetic_presentation = true;
|
||||
check_arithmetic_type();
|
||||
Handler::on_alt_form();
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_zero_fill(void){
|
||||
requires_arithmetic_presentation = true;
|
||||
check_arithmetic_type();
|
||||
Handler::on_zero_fill();
|
||||
}
|
||||
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::on_type_option(int type){
|
||||
presentation& chosen = this->specs.present;
|
||||
|
||||
switch(type){
|
||||
case 0:
|
||||
break;
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'd':
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
chosen = presentation::int_t;
|
||||
break;
|
||||
case 'c':
|
||||
chosen = presentation::char_t;
|
||||
break;
|
||||
case 's':
|
||||
chosen = presentation::string_t;
|
||||
break;
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
chosen = presentation::float_t;
|
||||
break;
|
||||
case 'p':
|
||||
chosen = presentation::ptr_t;
|
||||
break;
|
||||
default:
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier");
|
||||
};
|
||||
|
||||
switch(arg_type){
|
||||
case storage_type::none_t:
|
||||
REXY_THROW_FORMAT_ERROR("Invalid argument");
|
||||
break;
|
||||
case storage_type::bool_t:
|
||||
if(chosen == presentation::default_t){
|
||||
chosen = presentation::string_t;
|
||||
}
|
||||
if(chosen != presentation::string_t && chosen != presentation::int_t && chosen != presentation::char_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier for bool");
|
||||
}
|
||||
break;
|
||||
case storage_type::char_t:
|
||||
if(chosen == presentation::default_t){
|
||||
chosen = presentation::char_t;
|
||||
}
|
||||
if(chosen != presentation::char_t && chosen != presentation::int_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier for character");
|
||||
}
|
||||
break;
|
||||
case storage_type::int_t:
|
||||
case storage_type::uint_t:
|
||||
case storage_type::long_long_t:
|
||||
case storage_type::ulong_long_t:
|
||||
if(chosen == presentation::default_t){
|
||||
chosen = presentation::int_t;
|
||||
}
|
||||
if(chosen != presentation::int_t && chosen != presentation::char_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier for integral");
|
||||
}
|
||||
break;
|
||||
case storage_type::float_t:
|
||||
case storage_type::double_t:
|
||||
case storage_type::long_double_t:
|
||||
if(chosen == presentation::default_t){
|
||||
chosen = presentation::float_t;
|
||||
}
|
||||
if(chosen != presentation::float_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier for floating point");
|
||||
}
|
||||
break;
|
||||
case storage_type::char_ptr_t:
|
||||
case storage_type::string_t:
|
||||
if(chosen == presentation::default_t){
|
||||
chosen = presentation::string_t;
|
||||
}
|
||||
if(chosen != presentation::string_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier for string");
|
||||
}
|
||||
break;
|
||||
case storage_type::ptr_t:
|
||||
if(chosen == presentation::default_t){
|
||||
chosen = presentation::ptr_t;
|
||||
}
|
||||
if(chosen != presentation::ptr_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier for pointer");
|
||||
}
|
||||
break;
|
||||
case storage_type::custom_t:
|
||||
break;
|
||||
};
|
||||
|
||||
if(requires_arithmetic_presentation && chosen != presentation::int_t && chosen != presentation::float_t){
|
||||
REXY_THROW_FORMAT_ERROR("Invalid type specifier with modifier requiring integer presentation");
|
||||
}
|
||||
return Handler::on_type_option(type);
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::check_precision(void){
|
||||
if(!(is_floating_storage_type(arg_type) || is_string_storage_type(arg_type))){
|
||||
REXY_THROW_FORMAT_ERROR("Precision not valid for argument type");
|
||||
}
|
||||
}
|
||||
template<class Handler>
|
||||
constexpr void format_specs_checker<Handler>::check_arithmetic_type(void){
|
||||
if(!is_integral_storage_type(arg_type)){
|
||||
REXY_THROW_FORMAT_ERROR("Formatting argument requires an arithmetic type");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
38
include/rexy/detail/format/standard_types.hpp
Normal file
38
include/rexy/detail/format/standard_types.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_STANDARD_TYPES_HPP
|
||||
#define REXY_DETAIL_FORMAT_STANDARD_TYPES_HPP
|
||||
|
||||
namespace rexy::fmt{
|
||||
|
||||
template<class T, class Char>
|
||||
class formatter;
|
||||
template<class Context>
|
||||
class basic_format_arg;
|
||||
template<class Context>
|
||||
class basic_format_args;
|
||||
template<class OutIt, class Char>
|
||||
class basic_format_context;
|
||||
template<class Char>
|
||||
class basic_format_parse_context;
|
||||
struct format_error;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
71
include/rexy/detail/format/storage.hpp
Normal file
71
include/rexy/detail/format/storage.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_STORAGE_HPP
|
||||
#define REXY_DETAIL_FORMAT_STORAGE_HPP
|
||||
|
||||
#include <type_traits> //declval
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
//tag for use in basic_format_arg(s)
|
||||
enum class storage_type{
|
||||
none_t = 0,
|
||||
|
||||
bool_t,
|
||||
char_t,
|
||||
int_t,
|
||||
uint_t,
|
||||
long_long_t,
|
||||
ulong_long_t,
|
||||
float_t,
|
||||
double_t,
|
||||
long_double_t,
|
||||
|
||||
char_ptr_t,
|
||||
string_t,
|
||||
ptr_t,
|
||||
|
||||
custom_t
|
||||
};
|
||||
constexpr bool is_integral_storage_type(storage_type t){
|
||||
return t >= storage_type::bool_t && t <= storage_type::long_double_t;
|
||||
}
|
||||
constexpr bool is_integer_storage_type(storage_type t){
|
||||
return t >= storage_type::bool_t && t <= storage_type::ulong_long_t;
|
||||
}
|
||||
constexpr bool is_floating_storage_type(storage_type t){
|
||||
return t >= storage_type::float_t && t <= storage_type::long_double_t;
|
||||
}
|
||||
constexpr bool is_string_storage_type(storage_type t){
|
||||
return t == storage_type::string_t || t == storage_type::char_ptr_t;
|
||||
}
|
||||
|
||||
//Mappings from actual type to the type used in basic_format_arg(s)
|
||||
template<class T, class Char>
|
||||
consteval storage_type map_to_storage_enum(void);
|
||||
template<class T, class Char>
|
||||
static constexpr storage_type map_to_storage_enum_v = map_to_storage_enum<T,Char>();
|
||||
template<class Context>
|
||||
struct map_to_stored_type_helper;
|
||||
template<class T, class Context>
|
||||
using stored_type_t = decltype(std::declval<map_to_stored_type_helper<Context>>()(std::declval<T>()));
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
114
include/rexy/detail/format/storage.tpp
Normal file
114
include/rexy/detail/format/storage.tpp
Normal file
@ -0,0 +1,114 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_STORAGE_TPP
|
||||
#define REXY_DETAIL_FORMAT_STORAGE_TPP
|
||||
|
||||
#include "storage.hpp"
|
||||
|
||||
#include "named_args.hpp"
|
||||
|
||||
#include <type_traits> //is_pointer, remove_cvref, remove_pointer, is_same, is_arithmetic
|
||||
#include <concepts> //unsigned_integral, signed_integral, floating_point
|
||||
#include <cstddef> //nullptr_t
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
///////////////////////storage type mapping////////////////////////
|
||||
template<class T, class Char>
|
||||
consteval storage_type map_to_storage_enum(void){
|
||||
using type = std::remove_cvref_t<T>;
|
||||
if constexpr(std::is_pointer_v<type>){
|
||||
if constexpr(std::is_same_v<std::remove_cv_t<std::remove_pointer_t<type>>,Char>){
|
||||
return storage_type::char_ptr_t;
|
||||
}else{
|
||||
return storage_type::ptr_t;
|
||||
}
|
||||
}else if constexpr(std::is_same_v<type,basic_string_view<Char>>){
|
||||
return storage_type::string_t;
|
||||
}else if constexpr(std::is_same_v<type,Char>){
|
||||
return storage_type::char_t;
|
||||
}else if constexpr(std::is_same_v<type,std::monostate>){
|
||||
return storage_type::none_t;
|
||||
}else if constexpr(std::is_same_v<type,bool>){
|
||||
return storage_type::bool_t;
|
||||
}else if constexpr(std::is_same_v<type,int>){
|
||||
return storage_type::int_t;
|
||||
}else if constexpr(std::is_same_v<type,unsigned int>){
|
||||
return storage_type::uint_t;
|
||||
}else if constexpr(std::is_same_v<type,long long>){
|
||||
return storage_type::long_long_t;
|
||||
}else if constexpr(std::is_same_v<type,unsigned long long>){
|
||||
return storage_type::ulong_long_t;
|
||||
}else if constexpr(std::is_same_v<type,float>){
|
||||
return storage_type::float_t;
|
||||
}else if constexpr(std::is_same_v<type,double>){
|
||||
return storage_type::double_t;
|
||||
}else if constexpr(std::is_same_v<type,long double>){
|
||||
return storage_type::long_double_t;
|
||||
}else{
|
||||
return storage_type::custom_t;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Context>
|
||||
struct map_to_stored_type_helper{
|
||||
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
template<class T>
|
||||
requires (!std::is_arithmetic_v<T> && !std::is_pointer_v<T> && !NamedArg<T>)
|
||||
constexpr auto operator()(T) -> typename basic_format_arg<Context>::handle;
|
||||
constexpr auto operator()(std::unsigned_integral auto i){
|
||||
if constexpr(sizeof(i) <= sizeof(unsigned int)){
|
||||
using uint = unsigned int;
|
||||
return uint{};
|
||||
}else{
|
||||
using ull = unsigned long long;
|
||||
return ull{};
|
||||
}
|
||||
}
|
||||
constexpr auto operator()(std::signed_integral auto i){
|
||||
if constexpr(sizeof(i) <= sizeof(int)){
|
||||
return int{};
|
||||
}else{
|
||||
using ll = long long;
|
||||
return ll{};
|
||||
}
|
||||
}
|
||||
template<std::floating_point T>
|
||||
constexpr auto operator()(T) -> std::remove_cvref_t<T>;
|
||||
constexpr auto operator()(char_type) -> char_type;
|
||||
constexpr auto operator()(bool) -> bool;
|
||||
constexpr auto operator()(float) -> float;
|
||||
constexpr auto operator()(double) -> double;
|
||||
constexpr auto operator()(long double) -> long double;
|
||||
constexpr auto operator()(const char_type*) -> const char_type*;
|
||||
constexpr auto operator()(basic_string_view<char_type>) -> basic_string_view<char_type>;
|
||||
constexpr auto operator()(basic_string<char_type>) -> basic_string_view<char_type>;
|
||||
template<class T>
|
||||
requires (!std::is_same_v<std::remove_cvref_t<T>,char_type>)
|
||||
constexpr auto operator()(T*) -> const void*;
|
||||
constexpr auto operator()(std::nullptr_t) -> const void*;
|
||||
template<NamedArg T>
|
||||
constexpr auto operator()(T t) -> decltype((*this)(t.value));
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
150
include/rexy/detail/format/traits.hpp
Normal file
150
include/rexy/detail/format/traits.hpp
Normal file
@ -0,0 +1,150 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_TRAITS_HPP
|
||||
#define REXY_DETAIL_FORMAT_TRAITS_HPP
|
||||
|
||||
#include <type_traits> //is_convertible, is_floating_point, integral_constant, is_same, remove_cvref
|
||||
#include <concepts> //integral, floating_point
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include "standard_types.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "named_args.hpp"
|
||||
#include "internal_types.hpp"
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
template<class T>
|
||||
struct valid_dynamic_index{
|
||||
static constexpr bool value = std::is_convertible_v<T,long long> && !std::is_floating_point_v<T>;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool valid_dynamic_index_v = valid_dynamic_index<T>::value;
|
||||
|
||||
template<class T>
|
||||
struct is_char_or_bool : public std::false_type{};
|
||||
template<>
|
||||
struct is_char_or_bool<bool> : public std::true_type{};
|
||||
template<>
|
||||
struct is_char_or_bool<char> : public std::true_type{};
|
||||
template<>
|
||||
struct is_char_or_bool<wchar_t> : public std::true_type{};
|
||||
|
||||
template<class T>
|
||||
concept Integral = std::integral<T> && !is_char_or_bool<T>::value;
|
||||
template<class T>
|
||||
concept Bool = std::is_same_v<std::remove_cvref_t<T>,bool>;
|
||||
template<class T>
|
||||
concept Floating = std::floating_point<T>;
|
||||
template<class T>
|
||||
concept Arithmetic = Integral<T> || Floating<T>;
|
||||
template<class T, class FmtCtx>
|
||||
concept Handle = std::is_same_v<std::remove_cvref_t<T>,typename basic_format_arg<FmtCtx>::handle>;
|
||||
template<class T>
|
||||
concept Formatter_Char = std::is_same_v<T,char> || std::is_same_v<T,wchar_t>;
|
||||
template<class T, class FmtCtx>
|
||||
struct is_handle{
|
||||
static constexpr bool value = Handle<T,FmtCtx>;
|
||||
};
|
||||
template<class T, class FmtCtx>
|
||||
static constexpr bool is_handle_v = is_handle<T,FmtCtx>::value;
|
||||
|
||||
template<class T>
|
||||
struct extract_char_type_from_context;
|
||||
template<class OutIt, class Char>
|
||||
struct extract_char_type_from_context<basic_format_context<OutIt,Char>>{
|
||||
using type = Char;
|
||||
};
|
||||
template<class T>
|
||||
using extract_char_type_from_context_t = typename extract_char_type_from_context<T>::type;
|
||||
|
||||
|
||||
template<std::size_t I, class FmtCtx, class Arg, class... Args>
|
||||
constexpr bool is_dynamic_integer_impl(int index){
|
||||
using mapped_t = stored_type_t<Arg,FmtCtx>;
|
||||
if(index == I){
|
||||
return valid_dynamic_index_v<mapped_t>;
|
||||
}
|
||||
if constexpr(sizeof...(Args) > 0){
|
||||
return is_dynamic_integer_impl<I+1,FmtCtx,Args...>(index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<class Char, class... Args>
|
||||
constexpr bool is_dynamic_integer(int index){
|
||||
using fmt_ctx_t = fmt_context_t<Char>;
|
||||
if constexpr(sizeof...(Args) == 0){
|
||||
return false;
|
||||
}else{
|
||||
return is_dynamic_integer_impl<0,fmt_ctx_t,Args...>(index);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr unsigned char utf_test[] = "\u00B5";
|
||||
constexpr bool is_utf8(void){
|
||||
using uchar = unsigned char;
|
||||
return (sizeof(utf_test) == 3) && (uchar(utf_test[0]) == 0xC2) && (uchar(utf_test[1]) == 0xB5);
|
||||
}
|
||||
static constexpr wchar_t wutf_test[] = L"\U00010437";
|
||||
constexpr bool is_wutf16(void){
|
||||
return (sizeof(wutf_test) / sizeof(wutf_test[0]) == 3 &&
|
||||
wutf_test[0] == 0xD801 && wutf_test[1] == 0xDC37);
|
||||
}
|
||||
constexpr bool is_wutf32(void){
|
||||
return (sizeof(wutf_test) / sizeof(wutf_test[0]) == 2 &&
|
||||
wutf_test[0] == 0x00010437);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct is_utf8_encoded_string_type : public std::false_type{};
|
||||
template<>
|
||||
struct is_utf8_encoded_string_type<char8_t> : public std::true_type{};
|
||||
template<>
|
||||
struct is_utf8_encoded_string_type<char> : public std::integral_constant<bool,is_utf8()>{};
|
||||
template<class T>
|
||||
static constexpr bool is_utf8_encoded_string_type_v = is_utf8_encoded_string_type<T>::value;
|
||||
|
||||
template<class T>
|
||||
struct is_utf16_encoded_string_type : public std::false_type{};
|
||||
template<>
|
||||
struct is_utf16_encoded_string_type<char16_t> : public std::true_type{};
|
||||
template<>
|
||||
struct is_utf16_encoded_string_type<wchar_t> : public std::integral_constant<bool,is_wutf16()>{};
|
||||
template<class T>
|
||||
static constexpr bool is_utf16_encoded_string_type_v = is_utf16_encoded_string_type<T>::value;
|
||||
|
||||
template<class T>
|
||||
struct is_utf32_encoded_string_type : public std::false_type{};
|
||||
template<>
|
||||
struct is_utf32_encoded_string_type<char32_t> : public std::true_type{};
|
||||
template<>
|
||||
struct is_utf32_encoded_string_type<wchar_t> : public std::integral_constant<bool,is_wutf32()>{};
|
||||
template<class T>
|
||||
static constexpr bool is_utf32_encoded_string_type_v = is_utf32_encoded_string_type<T>::value;
|
||||
|
||||
template<class T>
|
||||
concept UTF8_String = is_utf8_encoded_string_type_v<T>;
|
||||
template<class T>
|
||||
concept UTF16_String = is_utf16_encoded_string_type_v<T>;
|
||||
template<class T>
|
||||
concept UTF32_String = is_utf32_encoded_string_type_v<T>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
91
include/rexy/detail/format/utf_iterator.hpp
Normal file
91
include/rexy/detail/format/utf_iterator.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_DETAIL_FORMAT_UTF_ITERATOR_TPP
|
||||
#define REXY_DETAIL_FORMAT_UTF_ITERATOR_TPP
|
||||
|
||||
namespace rexy::fmt::detail{
|
||||
|
||||
template<class Char>
|
||||
class utf8_iterator
|
||||
{
|
||||
private:
|
||||
const Char* m_start = nullptr;
|
||||
const Char* m_last = nullptr;
|
||||
const Char* m_next = nullptr;
|
||||
char32_t m_value = 0;
|
||||
|
||||
public:
|
||||
constexpr utf8_iterator(const Char* first, const Char* last):
|
||||
m_start(first), m_last(last)
|
||||
{
|
||||
m_next = convert_codepoint(m_start, m_last, m_value);
|
||||
}
|
||||
|
||||
constexpr utf8_iterator& operator++(void){
|
||||
m_start = m_next;
|
||||
if(m_start != m_last){
|
||||
m_next = convert_codepoint(m_start, m_last, m_value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
constexpr utf8_iterator operator++(int){
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
constexpr char32_t operator*(void)const{
|
||||
return m_value;
|
||||
}
|
||||
constexpr bool valid(void)const{
|
||||
return m_start != m_last;
|
||||
}
|
||||
constexpr std::size_t byte_count(void)const{
|
||||
return m_next - m_start;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr const Char* convert_codepoint(const Char* first, const Char* last, char32_t& codepoint){
|
||||
const std::size_t maxlen = last - first;
|
||||
if((*first & 0x80) == 0){
|
||||
codepoint = first[0];
|
||||
return first + 1;
|
||||
}else if((*first & 0xE0) == 0xC0 && maxlen > 1){
|
||||
codepoint = ((first[0] & 0x1F) << 6);
|
||||
codepoint += (first[1] & 0x3F);
|
||||
return first + 2;
|
||||
}else if((*first & 0xF0) == 0xE0 && maxlen > 2){
|
||||
codepoint = ((first[0] & 0x0F) << 12);
|
||||
codepoint += ((first[1] & 0x3F) << 6);
|
||||
codepoint += (first[2] & 0x3F);
|
||||
return first + 3;
|
||||
}else if((*first & 0xF8) == 0xF0 && maxlen > 3){
|
||||
codepoint = ((first[0] & 0x07) << 18);
|
||||
codepoint += ((first[1] & 0x3F) << 12);
|
||||
codepoint += (first[2] & 0x3F) << 6;
|
||||
codepoint += (first[3] & 0x3F);
|
||||
return first + 4;
|
||||
}
|
||||
codepoint = 0;
|
||||
return first;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -19,28 +19,24 @@
|
||||
#ifndef REXY_DETAIL_HASALLOCATOR_HPP
|
||||
#define REXY_DETAIL_HASALLOCATOR_HPP
|
||||
|
||||
#include "../compat/standard.hpp"
|
||||
#include "../allocator.hpp"
|
||||
|
||||
#include <type_traits> //is_empty
|
||||
|
||||
namespace rexy::detail{
|
||||
|
||||
template<class Alloc>
|
||||
struct hasallocator
|
||||
{
|
||||
Alloc m_alloc;
|
||||
|
||||
auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){
|
||||
return m_alloc.allocate(bytes);
|
||||
template<REXY_ALLOCATOR_CONCEPT Alloc>
|
||||
struct hasallocator : public Alloc{
|
||||
using Alloc::Alloc;
|
||||
using Alloc::operator=;
|
||||
constexpr Alloc& allocator(void){
|
||||
return *this;
|
||||
}
|
||||
void deallocate(typename Alloc::pointer p, typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.deallocate(nullptr,0))){
|
||||
m_alloc.deallocate(p, bytes);
|
||||
}
|
||||
|
||||
Alloc& allocator(void){
|
||||
return m_alloc;
|
||||
}
|
||||
const Alloc& allocator(void)const{
|
||||
return m_alloc;
|
||||
constexpr const Alloc& allocator(void)const{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
#define REXY_STRING_APPENDER_HPP
|
||||
|
||||
#include "../expression.hpp"
|
||||
#include "../traits.hpp"
|
||||
#include <utility> //forward
|
||||
#include <type_traits> //enable_if, declval
|
||||
|
||||
namespace rexy::detail{
|
||||
|
||||
@ -49,12 +49,7 @@ namespace rexy::detail{
|
||||
template<class Val, std::enable_if_t<!rexy::is_template_derived_type<Val,binary_expression>::value,int> = 0, class = decltype(std::declval<Val>().length())>
|
||||
constexpr void operator()(Val&& v)
|
||||
{
|
||||
m_targ.append(std::forward<Val>(v).get(), std::forward<Val>(v).length());
|
||||
}
|
||||
template<class Val, std::enable_if_t<!rexy::is_template_derived_type<Val,binary_expression>::value,void*> = nullptr, class = decltype(std::declval<Val>().size())>
|
||||
constexpr void operator()(Val&& v)
|
||||
{
|
||||
m_targ.append(std::forward<Val>(v).get(), std::forward<Val>(v).size());
|
||||
m_targ.append(std::forward<Val>(v).data(), std::forward<Val>(v).length());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
215
include/rexy/enum_traits.hpp
Normal file
215
include/rexy/enum_traits.hpp
Normal file
@ -0,0 +1,215 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//requires c++20
|
||||
|
||||
#ifndef REXY_ENUM_CLASS_HPP
|
||||
#define REXY_ENUM_CLASS_HPP
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
#ifndef __cpp_concepts
|
||||
|
||||
#error "Cannot use enum_traits without C++20 concept support"
|
||||
|
||||
#else //__cpp_concepts
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template<class T>
|
||||
concept Scoped_Enum = requires{
|
||||
typename std::underlying_type_t<T>;
|
||||
requires std::is_enum_v<T>;
|
||||
requires !std::is_convertible_v<T, std::underlying_type_t<T>>;
|
||||
};
|
||||
|
||||
|
||||
//since std::to_underlying is only available since c++23
|
||||
namespace rexy::enum_traits{
|
||||
template<Scoped_Enum E>
|
||||
constexpr std::underlying_type_t<E> to_underlying(E t)noexcept{
|
||||
return static_cast<std::underlying_type_t<E>>(t);
|
||||
}
|
||||
}
|
||||
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator|(E t, E t2)noexcept{
|
||||
return static_cast<E>(rexy::enum_traits::to_underlying(t) | rexy::enum_traits::to_underlying(t2));
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E& operator|=(E& t, E t2)noexcept{
|
||||
t = static_cast<E>(rexy::enum_traits::to_underlying(t) | rexy::enum_traits::to_underlying(t2));
|
||||
return t;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator&(E t, E t2)noexcept{
|
||||
return static_cast<E>(rexy::enum_traits::to_underlying(t) & rexy::enum_traits::to_underlying(t2));
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E& operator&=(E& t, E t2)noexcept{
|
||||
t = static_cast<E>(rexy::enum_traits::to_underlying(t) & rexy::enum_traits::to_underlying(t2));
|
||||
return t;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator^(E t, E t2)noexcept{
|
||||
return static_cast<E>(rexy::enum_traits::to_underlying(t) ^ rexy::enum_traits::to_underlying(t2));
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E& operator^=(E& t, E t2)noexcept{
|
||||
t = static_cast<E>(rexy::enum_traits::to_underlying(t) ^ rexy::enum_traits::to_underlying(t2));
|
||||
return t;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator!(E t)noexcept{
|
||||
return static_cast<E>(!(rexy::enum_traits::to_underlying(t)));
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator~(E t)noexcept{
|
||||
return static_cast<E>(~rexy::enum_traits::to_underlying(t));
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator<<(E t, int shift)noexcept{
|
||||
return static_cast<E>(rexy::enum_traits::to_underlying(t) << shift);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E& operator<<=(E& t, int shift)noexcept{
|
||||
t = static_cast<E>(rexy::enum_traits::to_underlying(t) << shift);
|
||||
return t;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E operator>>(E t, int shift)noexcept{
|
||||
return static_cast<E>(rexy::enum_traits::to_underlying(t) >> shift);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr E& operator>>=(E& t, int shift)noexcept{
|
||||
t = static_cast<E>(rexy::enum_traits::to_underlying(t) >> shift);
|
||||
return t;
|
||||
}
|
||||
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator>(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) > rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator>(E t, std::underlying_type_t<E> u){
|
||||
return rexy::enum_traits::to_underlying(t) > u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator<(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) < rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator<(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) < u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator>=(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) >= rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator>=(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) >= u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator<=(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) <= rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator<=(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) <= u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator==(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) == rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator==(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) == u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator!=(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) != rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool operator!=(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) != u;
|
||||
}
|
||||
|
||||
namespace rexy::enum_traits{
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool not_zero(E t)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) != 0;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_zero(E t)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) == 0;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_greater_than(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) > rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_greater_than(E t, std::underlying_type_t<E> u){
|
||||
return rexy::enum_traits::to_underlying(t) > u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_less_than(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) < rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_less_than(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) < u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_greater_than_equal(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) >= rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_greater_than_equal(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) >= u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_less_than_equal(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) <= rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_less_than_equal(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) <= u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_equal(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) == rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_equal(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) == u;
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_not_equal(E t1, E t2)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t1) != rexy::enum_traits::to_underlying(t2);
|
||||
}
|
||||
template<Scoped_Enum E>
|
||||
constexpr bool is_not_equal(E t, std::underlying_type_t<E> u)noexcept{
|
||||
return rexy::enum_traits::to_underlying(t) != u;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__cpp_concepts
|
||||
|
||||
#endif
|
||||
@ -19,7 +19,7 @@
|
||||
#ifndef REXY_EXPRESSION_HPP
|
||||
#define REXY_EXPRESSION_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <type_traits> //is_void, remove_reference, conditional
|
||||
#include <utility> //forward
|
||||
|
||||
#include "rexy.hpp"
|
||||
@ -29,8 +29,8 @@ namespace rexy{
|
||||
template<class L, class R>
|
||||
class binary_expression
|
||||
{
|
||||
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Left value of rexy::binary_expression cannot be void!");
|
||||
static_assert(!std::is_same<std::decay_t<R>,void>::value, "Right value of rexy::binary_expression cannot be void!");
|
||||
static_assert(!std::is_void_v<L>, "Left value of rexy::binary_expression cannot be void!");
|
||||
static_assert(!std::is_void_v<R>, "Right value of rexy::binary_expression cannot be void!");
|
||||
|
||||
public:
|
||||
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
||||
@ -78,7 +78,7 @@ namespace rexy{
|
||||
template<class L>
|
||||
class unary_expression
|
||||
{
|
||||
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Value of rexy::unary_expression cannot be void!");
|
||||
static_assert(!std::is_void_v<L>, "Value of rexy::unary_expression cannot be void!");
|
||||
|
||||
public:
|
||||
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -19,15 +19,18 @@
|
||||
#ifndef REXY_FILERD_HPP
|
||||
#define REXY_FILERD_HPP
|
||||
|
||||
#ifndef LIBREXY_HEADER_ONLY
|
||||
|
||||
#include <cstdio> //FILE
|
||||
#include <cstddef> //size_t
|
||||
#include <type_traits> //is_nothrow_constructible
|
||||
|
||||
#include "string.hpp"
|
||||
#include "steal.hpp"
|
||||
#include "binary.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "rexy.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "string_view.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
@ -35,7 +38,8 @@ namespace rexy{
|
||||
class filerd
|
||||
{
|
||||
private:
|
||||
FILE* m_fp = nullptr;
|
||||
std::FILE* m_fp = nullptr;
|
||||
bool m_finished = false;
|
||||
|
||||
public:
|
||||
constexpr filerd(void)noexcept = default;
|
||||
@ -46,31 +50,39 @@ namespace rexy{
|
||||
~filerd(void)noexcept;
|
||||
filerd& operator=(const filerd&) = delete;
|
||||
constexpr filerd& operator=(filerd&& f)noexcept{
|
||||
swap(m_fp, f.m_fp);
|
||||
rexy::swap(m_fp, f.m_fp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(FILE* fp = nullptr)noexcept;
|
||||
FILE* release(void)noexcept;
|
||||
size_t length(void)noexcept;
|
||||
size_t position(void)const noexcept;
|
||||
void rewind(size_t pos = 0)noexcept;
|
||||
void reset(std::FILE* fp = nullptr)noexcept;
|
||||
std::FILE* release(void)noexcept;
|
||||
std::size_t length(void)noexcept;
|
||||
std::size_t position(void)const noexcept;
|
||||
void rewind(std::size_t pos = 0)noexcept;
|
||||
|
||||
operator FILE*(void)noexcept;
|
||||
operator const FILE*(void)const noexcept;
|
||||
FILE* get(void)noexcept;
|
||||
const FILE* get(void)const noexcept;
|
||||
operator std::FILE*(void)noexcept;
|
||||
operator const std::FILE*(void)const noexcept;
|
||||
std::FILE* get(void)noexcept;
|
||||
const std::FILE* get(void)const noexcept;
|
||||
operator bool(void)const noexcept;
|
||||
|
||||
size_t read(char* dest, size_t bytes)noexcept;
|
||||
rexy::string read(size_t bytes)noexcept;
|
||||
rexy::string readln(size_t max = 0)noexcept;
|
||||
rexy::binary read_bin(size_t bytes)noexcept(std::is_nothrow_constructible<rexy::binary, rexy::steal<char*>, size_t, size_t>::value);
|
||||
bool eof(void)const;
|
||||
|
||||
size_t write(const char* c, size_t bytes)noexcept;
|
||||
size_t write(const rexy::string_base<char>& s)noexcept;
|
||||
std::size_t read(char* dest, std::size_t bytes)noexcept;
|
||||
rexy::string read(std::size_t bytes)noexcept;
|
||||
rexy::string readln(std::size_t max = 0)noexcept;
|
||||
rexy::buffer<char> read_bin(std::size_t bytes)noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, std::size_t>::value);
|
||||
|
||||
std::size_t write(const char* c, std::size_t bytes)noexcept;
|
||||
std::size_t write(rexy::string_view s)noexcept;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#else //LIBREXY_HEADER_ONLY
|
||||
|
||||
#error "rexy::filerd is not available when built with header only support"
|
||||
|
||||
#endif //LIBREXY_HEADER_ONLY
|
||||
|
||||
#endif
|
||||
|
||||
157
include/rexy/format.hpp
Normal file
157
include/rexy/format.hpp
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_FORMAT_HPP
|
||||
#define REXY_FORMAT_HPP
|
||||
|
||||
#include "string.hpp"
|
||||
#include "string_view.hpp"
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
REXY_REQUIRES_CPP20;
|
||||
|
||||
#ifndef REXY_STANDARD_CPP20
|
||||
//Maybe I'll make it work someday, but not for now
|
||||
#error "Cannot use formatting library without C++20 support"
|
||||
#endif
|
||||
|
||||
#include "detail/format/standard_types.hpp"
|
||||
#include "detail/format/internal_types.hpp"
|
||||
#include "detail/format/arg_store.hpp"
|
||||
#include "detail/format/format_string.hpp"
|
||||
#include "detail/format/format_args.hpp"
|
||||
#include "detail/format/format_error.hpp"
|
||||
|
||||
#include <cstdio> //FILE
|
||||
|
||||
#include <cstddef> //size_t
|
||||
#include <locale> //locale
|
||||
|
||||
namespace rexy{
|
||||
|
||||
//Alias declarations of standard defined types
|
||||
using format_context = fmt::detail::fmt_context_t<char>;
|
||||
using wformat_context = fmt::detail::fmt_context_t<wchar_t>;
|
||||
using parse_context = fmt::detail::parse_context_t<char>;
|
||||
using wparse_context = fmt::detail::parse_context_t<wchar_t>;
|
||||
using format_arg = fmt::basic_format_arg<format_context>;
|
||||
using wformat_arg = fmt::basic_format_arg<wformat_context>;
|
||||
using format_args = fmt::basic_format_args<format_context>;
|
||||
using wformat_args = fmt::basic_format_args<wformat_context>;
|
||||
|
||||
template<class Context = format_context, class... Args>
|
||||
fmt::detail::basic_format_arg_store<Context,Args...> make_format_args(Args&&... args);
|
||||
template<class Context = wformat_context, class... Args>
|
||||
fmt::detail::basic_format_arg_store<Context,Args...> make_wformat_args(Args&&... args);
|
||||
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, string_view fmt, format_args args);
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, const std::locale& loc, string_view fmt, wformat_args args);
|
||||
|
||||
string vformat(string_view fmt, format_args args);
|
||||
string vformat(const std::locale& loc, string_view fmt, format_args args);
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class... Args>
|
||||
string format(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args>
|
||||
string format(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
|
||||
|
||||
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, wstring_view fmt, wformat_args args);
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, const std::locale& loc, wstring_view fmt, wformat_args args);
|
||||
|
||||
wstring vformat(wstring_view fmt, format_args args);
|
||||
wstring vformat(const std::locale& loc, wstring_view fmt, wformat_args args);
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class... Args>
|
||||
wstring format(fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args>
|
||||
wstring format(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::size_t print(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args>
|
||||
std::size_t print(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
template<class... Args>
|
||||
std::size_t println(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args>
|
||||
std::size_t println(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
std::size_t vprint_unicode(string_view fmt, format_args args);
|
||||
std::size_t vprint_unicode(FILE* stream, string_view fmt, format_args args);
|
||||
|
||||
|
||||
|
||||
template<class T, class Char>
|
||||
constexpr auto arg(const Char* name, T&& t);
|
||||
template<class T, class Char>
|
||||
constexpr auto arg(rexy::basic_string_view<Char> name, T&& t);
|
||||
template<rexy::cx::string Name, class T>
|
||||
constexpr auto arg(T&& t);
|
||||
|
||||
inline namespace fmt_literals{
|
||||
template<rexy::cx::string Name>
|
||||
constexpr auto operator""_a(void);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#include "format.tpp"
|
||||
|
||||
#endif
|
||||
340
include/rexy/format.tpp
Normal file
340
include/rexy/format.tpp
Normal file
@ -0,0 +1,340 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_FORMAT_TPP
|
||||
#define REXY_FORMAT_TPP
|
||||
|
||||
REXY_REQUIRES_CPP20;
|
||||
|
||||
#include "detail/format/format_args.tpp"
|
||||
#include "detail/format/formatter.tpp"
|
||||
#include "detail/format/storage.tpp"
|
||||
#include "detail/format/format_string.tpp"
|
||||
#include "detail/format/arg_store.tpp"
|
||||
#include "detail/format/output_buffer.tpp"
|
||||
#include "detail/format/basic_types.tpp"
|
||||
#include "detail/format/specs_handler.tpp"
|
||||
#include "detail/format/parse.tpp"
|
||||
#include "detail/format/context_handler.tpp"
|
||||
#include "detail/format/named_args.tpp"
|
||||
#include "detail/format/parse_context.tpp"
|
||||
#include "detail/format/format_context.tpp"
|
||||
|
||||
#include <iterator> //back_insert_iterator
|
||||
#include <utility> //move, forward
|
||||
#include <type_traits> //is_same
|
||||
#include <locale> //locale
|
||||
#include <cstddef> //size_t
|
||||
|
||||
namespace rexy{
|
||||
|
||||
namespace fmt::detail{
|
||||
|
||||
template<class Char, class OutIt>
|
||||
OutIt vformat_to(OutIt out, basic_string_view<std::type_identity_t<Char>> fmt, impl_format_args<std::type_identity_t<Char>> args){
|
||||
using char_type = Char;
|
||||
using format_handler_t = fmt::detail::format_handler<char_type>;
|
||||
|
||||
if constexpr(std::is_same_v<OutIt,fmt::detail::output_iterator_t<char_type>>){
|
||||
|
||||
fmt::detail::format_handler<char_type> handler{out, fmt, args};
|
||||
fmt::detail::parse::perform_parse(handler, fmt);
|
||||
|
||||
return std::move(out);
|
||||
}else{
|
||||
|
||||
fmt::detail::basic_format_output_buffer<char_type,OutIt> outbuf{out};
|
||||
fmt::detail::format_handler<char_type> handler{fmt::detail::output_iterator_t<char_type>{outbuf}, fmt, args};
|
||||
|
||||
fmt::detail::parse::perform_parse(handler, fmt);
|
||||
|
||||
return std::move(outbuf.out());
|
||||
}
|
||||
|
||||
}
|
||||
template<class Char, class OutIt>
|
||||
OutIt vformat_to(OutIt out, const std::locale& loc, basic_string_view<std::type_identity_t<Char>> fmt, impl_format_args<std::type_identity_t<Char>> args){
|
||||
using char_type = Char;
|
||||
using format_handler_t = fmt::detail::format_handler<char_type>;
|
||||
|
||||
if constexpr(std::is_same_v<OutIt,fmt::detail::output_iterator_t<char_type>>){
|
||||
|
||||
fmt::detail::format_handler<char_type> handler{out, fmt, args, loc};
|
||||
fmt::detail::parse::perform_parse(handler, fmt);
|
||||
|
||||
return std::move(out);
|
||||
}else{
|
||||
|
||||
fmt::detail::basic_format_output_buffer<char_type,OutIt> outbuf{out};
|
||||
fmt::detail::format_handler<char_type> handler{fmt::detail::output_iterator_t<char_type>{outbuf}, fmt, args, loc};
|
||||
|
||||
fmt::detail::parse::perform_parse(handler, fmt);
|
||||
|
||||
return std::move(outbuf.out());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class Char, class... Args>
|
||||
basic_format_arg_store<fmt_context_t<Char>,Args...> make_format_args(Args&&... args){
|
||||
return basic_format_arg_store<fmt_context_t<Char>,Args...>{std::forward<Args>(args)...};
|
||||
}
|
||||
}
|
||||
|
||||
template<class Context, class... Args>
|
||||
fmt::detail::basic_format_arg_store<Context,Args...> make_format_args(Args&&... args){
|
||||
return fmt::detail::basic_format_arg_store<Context,Args...>{std::forward<Args>(args)...};
|
||||
}
|
||||
template<class Context, class... Args>
|
||||
fmt::detail::basic_format_arg_store<Context,Args...> make_wformat_args(Args&&... args){
|
||||
return fmt::detail::basic_format_arg_store<Context,Args...>{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////char overloads///////////////////////////////////////////////
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, string_view fmt, format_args args){
|
||||
return fmt::detail::vformat_to<char>(std::move(out), fmt, std::move(args));
|
||||
}
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, const std::locale& loc, string_view fmt, format_args args){
|
||||
return fmt::detail::vformat_to<char>(std::move(out), loc, fmt, std::move(args));
|
||||
}
|
||||
|
||||
string vformat(string_view fmt, format_args args){
|
||||
using outit_t = std::back_insert_iterator<string>;
|
||||
string output;
|
||||
outit_t out{output};
|
||||
vformat_to(std::move(out), fmt, std::move(args));
|
||||
return output;
|
||||
}
|
||||
|
||||
string vformat(const std::locale& loc, string_view fmt, format_args args){
|
||||
using outit_t = std::back_insert_iterator<string>;
|
||||
string output;
|
||||
outit_t out{output};
|
||||
vformat_to(std::move(out), loc, fmt, std::move(args));
|
||||
return output;
|
||||
}
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
return vformat_to(std::move(out), fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
return vformat_to(std::move(out), loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::basic_format_output_n_buffer<char,OutIt> outbuf{std::move(out), max};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{outbuf}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return {outbuf.out(), outbuf.count()};
|
||||
}
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::basic_format_output_n_buffer<char,OutIt> outbuf{std::move(out), max};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{outbuf}, loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return {outbuf.out(), outbuf.count()};
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
string format(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
using outit_t = std::back_insert_iterator<string>;
|
||||
string output;
|
||||
outit_t out{output};
|
||||
vformat_to(std::move(out), fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return output;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
string format(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
using outit_t = std::back_insert_iterator<string>;
|
||||
string output;
|
||||
outit_t out{output};
|
||||
vformat_to(std::move(out), loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return output;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::basic_format_size_buffer<char> counter;
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{counter}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return counter.count();
|
||||
}
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::basic_format_size_buffer<char> counter;
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{counter}, loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return counter.count();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////wchar_t overloads///////////////////////////////////////////////
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, wstring_view fmt, wformat_args args){
|
||||
return fmt::detail::vformat_to<wchar_t>(std::move(out), fmt, std::move(args));
|
||||
}
|
||||
template<class OutIt>
|
||||
OutIt vformat_to(OutIt out, const std::locale& loc, wstring_view fmt, wformat_args args){
|
||||
return fmt::detail::vformat_to<wchar_t>(std::move(out), loc, fmt, std::move(args));
|
||||
}
|
||||
|
||||
wstring vformat(wstring_view fmt, wformat_args args){
|
||||
using outit_t = std::back_insert_iterator<wstring>;
|
||||
wstring output;
|
||||
outit_t out{output};
|
||||
vformat_to(std::move(out), fmt, std::move(args));
|
||||
return output;
|
||||
}
|
||||
wstring vformat(const std::locale& loc, wstring_view fmt, wformat_args args){
|
||||
using outit_t = std::back_insert_iterator<wstring>;
|
||||
wstring output;
|
||||
outit_t out{output};
|
||||
vformat_to(std::move(out), loc, fmt, std::move(args));
|
||||
return output;
|
||||
}
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
return vformat_to(std::move(out), fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||
}
|
||||
template<class OutIt, class... Args>
|
||||
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
return vformat_to(std::move(out), loc, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::basic_format_output_n_buffer<wchar_t,OutIt> outbuf{std::move(out), max};
|
||||
vformat_to(fmt::detail::output_iterator_t<wchar_t>{outbuf}, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||
return {outbuf.out(), outbuf.count()};
|
||||
}
|
||||
template<class OutIt, class... Args>
|
||||
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::basic_format_output_n_buffer<wchar_t,OutIt> outbuf{std::move(out), max};
|
||||
vformat_to(fmt::detail::output_iterator_t<wchar_t>{outbuf}, loc, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||
return {outbuf.out(), outbuf.count()};
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
wstring format(fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
using outit_t = std::back_insert_iterator<wstring>;
|
||||
wstring output;
|
||||
outit_t out{output};
|
||||
vformat_to(out, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||
return output;
|
||||
}
|
||||
template<class... Args>
|
||||
wstring format(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
using outit_t = std::back_insert_iterator<wstring>;
|
||||
wstring output;
|
||||
outit_t out{output};
|
||||
vformat_to(out, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||
return output;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||
|
||||
}
|
||||
template<class... Args>
|
||||
std::size_t formatted_size(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////print char overloads///////////////////////////////////////////////
|
||||
template<class... Args>
|
||||
std::size_t print(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::print_format_buffer<char> out{stdout};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return out.count();
|
||||
}
|
||||
template<class... Args>
|
||||
std::size_t print(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::print_format_buffer<char> out{stream};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
return out.count();
|
||||
}
|
||||
template<class... Args>
|
||||
std::size_t println(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::print_format_buffer<char> out{stdout};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
out.push_back('\n');
|
||||
return out.count();
|
||||
}
|
||||
template<class... Args>
|
||||
std::size_t println(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||
fmt::detail::print_format_buffer<char> out{stream};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||
out.push_back('\n');
|
||||
return out.count();
|
||||
}
|
||||
std::size_t vprint_unicode(string_view fmt, format_args args){
|
||||
fmt::detail::print_format_buffer<char> out{stdout};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt, args);
|
||||
return out.count();
|
||||
}
|
||||
std::size_t vprint_unicode(FILE* stream, string_view fmt, format_args args){
|
||||
fmt::detail::print_format_buffer<char> out{stream};
|
||||
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt, args);
|
||||
return out.count();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//creates a runtime argument from a c string
|
||||
template<class T, class Char>
|
||||
constexpr auto arg(const Char* name, T&& t){
|
||||
return fmt::detail::runtime_arg<T,Char>{std::forward<T>(t), name};
|
||||
}
|
||||
|
||||
//creates a runtime argument from a string view
|
||||
template<class T, class Char>
|
||||
constexpr auto arg(rexy::basic_string_view<Char> name, T&& t){
|
||||
return fmt::detail::runtime_arg{std::forward<T>(t), name};
|
||||
}
|
||||
|
||||
//create a compile time argument from a string literal
|
||||
template<rexy::cx::string Name, class T>
|
||||
constexpr auto arg(T&& t){
|
||||
return fmt::detail::static_arg<T,Name>{t};
|
||||
}
|
||||
|
||||
inline namespace fmt_literals{
|
||||
//create a compile time argument from a string literal
|
||||
template<rexy::cx::string Name>
|
||||
constexpr auto operator""_a(void){
|
||||
return fmt::detail::arg_literal_result<Name>{};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef REXY_REAL_STRINGIFY
|
||||
#undef REXY_STRINGIFY
|
||||
#undef REXY_THROW_FORMAT_ERROR
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -20,17 +20,29 @@
|
||||
#define REXY_HASH_HPP
|
||||
|
||||
#include <climits> //CHAR_BIT
|
||||
#include <cstddef> //size_t
|
||||
#include "rexy.hpp"
|
||||
|
||||
#include "allocator.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
|
||||
class basic_string;
|
||||
template<class Char>
|
||||
class basic_string_view;
|
||||
namespace cx{
|
||||
template<std::size_t N, class Char>
|
||||
class string;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct hash{
|
||||
constexpr size_t operator()(const T& t, size_t salt)const noexcept{
|
||||
constexpr size_t bytes = sizeof(size_t);
|
||||
size_t val = static_cast<size_t>(t);
|
||||
size_t hash = 5381 + salt; //magic hash number
|
||||
for(size_t i = 0;i < bytes;++i){
|
||||
constexpr std::size_t operator()(const T& t, std::size_t salt = 0)const noexcept{
|
||||
constexpr std::size_t bytes = sizeof(std::size_t);
|
||||
std::size_t val = static_cast<std::size_t>(t);
|
||||
std::size_t hash = 5381 + salt; //magic hash number
|
||||
for(std::size_t i = 0;i < bytes;++i){
|
||||
unsigned char c = static_cast<unsigned char>(val >> (i * CHAR_BIT));
|
||||
hash = ((hash << 5) + hash) ^ c;
|
||||
}
|
||||
@ -38,16 +50,32 @@ namespace rexy{
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail{
|
||||
//jenkins one at a time hash
|
||||
template<class Str>
|
||||
struct string_hash{
|
||||
constexpr std::size_t operator()(const Str& s, std::size_t salt = 0)const noexcept{
|
||||
std::size_t hash = salt;
|
||||
for(std::size_t i = 0;i < s.length();++i){
|
||||
hash += s[i];
|
||||
hash += (hash << 10);
|
||||
hash += (hash >> 6);
|
||||
}
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash << 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
|
||||
struct hash<rexy::basic_string<Char,Alloc>> : public detail::string_hash<rexy::basic_string<Char,Alloc>>{};
|
||||
template<class Char>
|
||||
struct hash<rexy::basic_string_view<Char>> : public detail::string_hash<rexy::basic_string_view<Char>>{};
|
||||
template<std::size_t N, class Char>
|
||||
struct hash<rexy::cx::string<N,Char>> : public detail::string_hash<rexy::cx::string<N,Char>>{};
|
||||
|
||||
}
|
||||
|
||||
#ifdef REXY_STRING_BASE_HPP
|
||||
#include "static_string_hash.hpp"
|
||||
#endif
|
||||
#ifdef REXY_STRING_HPP
|
||||
#include "basic_string_hash.hpp"
|
||||
#endif
|
||||
#ifdef REXY_CX_STRING_HPP
|
||||
#include "cx_string_hash.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
400
include/rexy/list.hpp
Normal file
400
include/rexy/list.hpp
Normal file
@ -0,0 +1,400 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_LLIST_HPP
|
||||
#define REXY_LLIST_HPP
|
||||
|
||||
#include <cstddef> //size_t, ptrdiff_t
|
||||
#include "allocator.hpp"
|
||||
#include "detail/hasallocator.hpp"
|
||||
#include "traits.hpp"
|
||||
#include <iterator> //bidirectional_iterator_tag, reverse_iterator
|
||||
#include <memory> //allocator_traits
|
||||
#include <initializer_list>
|
||||
#include <utility> //pair
|
||||
#include <type_traits>
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T, class Alloc>
|
||||
class list;
|
||||
|
||||
namespace detail{
|
||||
struct list_node_base{
|
||||
list_node_base* next = nullptr;
|
||||
list_node_base* prev = nullptr;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct list_iterator;
|
||||
template<class T>
|
||||
struct const_list_iterator;
|
||||
template<class T>
|
||||
struct list_node;
|
||||
}
|
||||
|
||||
template<class T, class Alloc = rexy::allocator<T>>
|
||||
class list : protected detail::hasallocator<typename std::allocator_traits<Alloc>::template rebind_alloc<detail::list_node<T>>>
|
||||
{
|
||||
private:
|
||||
using node_allocator_type = detail::hasallocator<typename std::allocator_traits<Alloc>::template rebind_alloc<detail::list_node<T>>>;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using allocator_type = Alloc;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using iterator = detail::list_iterator<T>;
|
||||
using const_iterator = detail::const_list_iterator<T>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
public:
|
||||
using node = detail::list_node<T>;
|
||||
|
||||
private:
|
||||
detail::list_node_base m_sentinel = {&m_sentinel, &m_sentinel};
|
||||
size_type m_size = 0;
|
||||
|
||||
public:
|
||||
constexpr list(void)noexcept = default;
|
||||
|
||||
constexpr explicit list(const allocator_type& alloc)noexcept;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list(size_type count, const_reference value = value_type(), const allocator_type& = allocator_type())noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
explicit list(size_type count, const allocator_type& alloc = allocator_type())noexcept(
|
||||
std::is_nothrow_default_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list(InputIt first, InputIt last, const allocator_type& alloc = allocator_type())noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list(const list& other, const allocator_type& alloc = allocator_type())noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list(list&& other, const allocator_type& alloc = allocator_type())noexcept;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list(std::initializer_list<value_type> l, const allocator_type& alloc = allocator_type())noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
~list(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list& operator=(const list& other)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list& operator=(list&& other)noexcept;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list& operator=(std::initializer_list<value_type> l)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
constexpr bool operator==(const list& other)const noexcept;
|
||||
#if __cpp_impl_three_way_comparison
|
||||
constexpr auto operator<=>(const list& other)const noexcept;
|
||||
#else
|
||||
constexpr bool operator!=(const list& other)const noexcept;
|
||||
constexpr bool operator<(const list& other)const noexcept;
|
||||
constexpr bool operator<=(const list& other)const noexcept;
|
||||
constexpr bool operator>(const list& other)const noexcept;
|
||||
constexpr bool operator>=(const list& other)const noexcept;
|
||||
#endif
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void assign(size_type count, const_reference value)noexcept(
|
||||
std::conditional_t<
|
||||
std::is_copy_assignable_v<value_type>,
|
||||
std::is_nothrow_copy_assignable<value_type>,
|
||||
std::true_type>::value &&
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void assign(InputIt first, InputIt last)noexcept(
|
||||
std::conditional_t<
|
||||
std::is_assignable_v<value_type, decltype(*first)>,
|
||||
std::is_nothrow_assignable<value_type, decltype(*first)>,
|
||||
std::true_type>::value &&
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void assign(std::initializer_list<value_type> l)noexcept(
|
||||
std::conditional_t<
|
||||
std::is_assignable_v<value_type, decltype(*l.begin())>,
|
||||
std::is_nothrow_assignable<value_type, decltype(*l.begin())>,
|
||||
std::true_type>::value &&
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
constexpr allocator_type get_allocator(void)const noexcept;
|
||||
|
||||
constexpr reference front(void)noexcept;
|
||||
constexpr const_reference front(void)const noexcept;
|
||||
constexpr reference back(void)noexcept;
|
||||
constexpr const_reference back(void)const noexcept;
|
||||
|
||||
constexpr iterator begin(void)noexcept;
|
||||
constexpr const_iterator begin(void)const noexcept;
|
||||
constexpr const_iterator cbegin(void)const noexcept;
|
||||
constexpr iterator end(void)noexcept;
|
||||
constexpr const_iterator end(void)const noexcept;
|
||||
constexpr const_iterator cend(void)const noexcept;
|
||||
|
||||
constexpr iterator next(iterator i)noexcept;
|
||||
constexpr const_iterator next(const_iterator i)noexcept;
|
||||
|
||||
constexpr iterator prev(iterator i)noexcept;
|
||||
constexpr const_iterator prev(const_iterator i)noexcept;
|
||||
|
||||
constexpr reverse_iterator rbegin(void)noexcept;
|
||||
constexpr const_reverse_iterator rbegin(void)const noexcept;
|
||||
constexpr const_reverse_iterator crbegin(void)const noexcept;
|
||||
constexpr reverse_iterator rend(void)noexcept;
|
||||
constexpr const_reverse_iterator rend(void)const noexcept;
|
||||
constexpr const_reverse_iterator crend(void)const noexcept;
|
||||
|
||||
constexpr bool empty(void)const noexcept;
|
||||
constexpr size_type size(void)const noexcept;
|
||||
constexpr size_type max_size(void)const noexcept;
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void clear(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator insert(const_iterator pos, const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator insert(const_iterator pos, value_type&& value)noexcept(
|
||||
std::is_nothrow_move_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator insert(const_iterator pos, size_type count, const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator insert(const_iterator pos, InputIt first, InputIt last)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator insert(const_iterator pos, std::initializer_list<value_type> l)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class... Args>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator emplace(const_iterator pos, Args&&... args)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, Args&&...> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator erase(const_iterator pos)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
iterator erase(const_iterator first, const_iterator last)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
reference push_back(const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
reference push_back(value_type&& value)noexcept(
|
||||
std::is_nothrow_move_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class... Args>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
reference emplace_back(Args&&... args)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, Args&&...> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void pop_back(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
reference push_front(const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
reference push_front(value_type&& value)noexcept(
|
||||
std::is_nothrow_move_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class... Args>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
reference emplace_front(Args&&... args)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, Args&&...> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void pop_front(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void resize(size_type count)noexcept(
|
||||
std::is_nothrow_default_constructible_v<value_type> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void resize(size_type count, value_type value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void swap(list& other)noexcept;
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void merge(list&& other)noexcept;
|
||||
template<class Comp>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void merge(list&& other, Comp comp)noexcept;
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void splice(const_iterator pos, list&& other)noexcept;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void splice(const_iterator pos, list&& other, const_iterator it)noexcept;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void splice(const_iterator pos, list&& other, const_iterator first, const_iterator last)noexcept;
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
size_type remove(const_reference value)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class UnaryPred>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
size_type remove_if(UnaryPred p)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void reverse(void)noexcept;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
size_type unique(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class BinaryPred>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
size_type unique(BinaryPred p)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void sort(void)noexcept;
|
||||
|
||||
template<class Comp>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void sort(Comp comp)noexcept;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void iterator_initialize_(InputIt first, InputIt last)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void constant_initialize_(size_type count, const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void default_initialize_(size_type count)noexcept(
|
||||
std::is_nothrow_default_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>);
|
||||
|
||||
template<class Comp>
|
||||
static iterator mergesort(iterator first, size_type firstlen, Comp comp)noexcept;
|
||||
static std::pair<iterator,size_type> ms_split(iterator it, size_type len)noexcept;
|
||||
|
||||
static void insert_node_(detail::list_node_base* prev, detail::list_node_base* n)noexcept;
|
||||
static void remove_node_(detail::list_node_base* rm)noexcept;
|
||||
static detail::list_node_base* get_next_then_move_node_(detail::list_node_base* dest, detail::list_node_base* n)noexcept;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "list.tpp"
|
||||
|
||||
#endif
|
||||
861
include/rexy/list.tpp
Normal file
861
include/rexy/list.tpp
Normal file
@ -0,0 +1,861 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_LLIST_TPP
|
||||
#define REXY_LLIST_TPP
|
||||
|
||||
#include <utility> //move, swap, forward
|
||||
#include <limits> //numeric_limits
|
||||
#include <algorithm> //lexicographical_compare_three_way, equal
|
||||
#include <memory> //construct_at, destroy_at
|
||||
|
||||
#include "utility.hpp" //sized_constant_iterator
|
||||
|
||||
namespace rexy{
|
||||
|
||||
namespace detail{
|
||||
template<class T>
|
||||
struct list_node : public list_node_base{
|
||||
T data;
|
||||
|
||||
constexpr list_node(list_node_base* next, list_node_base* prev, const T& d):
|
||||
list_node_base{next, prev},
|
||||
data(d){}
|
||||
constexpr list_node(list_node_base* next, list_node_base* prev, T&& d):
|
||||
list_node_base{next, prev},
|
||||
data(std::move(d)){}
|
||||
template<class... Args>
|
||||
constexpr list_node(list_node_base* next, list_node_base* prev, Args&&... args):
|
||||
list_node_base{next, prev},
|
||||
data(std::forward<Args>(args)...){}
|
||||
constexpr list_node(const list_node& n):
|
||||
list_node_base(n),
|
||||
data(n.data){}
|
||||
constexpr list_node(list_node&& n):
|
||||
list_node_base(std::move(n)),
|
||||
data(std::move(n.data)){}
|
||||
|
||||
constexpr list_node* next(void)const{return static_cast<list_node*>(list_node_base::next);}
|
||||
constexpr list_node* prev(void)const{return static_cast<list_node*>(list_node_base::prev);}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct list_iterator{
|
||||
|
||||
using value_type = T;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
|
||||
using node_t = list_node<T>;
|
||||
|
||||
list_node_base* current = nullptr;
|
||||
|
||||
constexpr bool operator==(const list_iterator& other)const noexcept{return current == other.current;}
|
||||
constexpr bool operator!=(const list_iterator& other)const noexcept{return current != other.current;}
|
||||
constexpr bool operator==(const const_list_iterator<T>& other)const noexcept{return current == other.nod();}
|
||||
constexpr bool operator!=(const const_list_iterator<T>& other)const noexcept{return current != other.nod();}
|
||||
|
||||
constexpr reference operator*(void){return static_cast<node_t*>(current)->data;}
|
||||
constexpr const_reference operator*(void)const{return static_cast<const node_t*>(current)->data;}
|
||||
constexpr pointer operator->(void){return &(static_cast<node_t*>(current)->data);}
|
||||
constexpr const_pointer operator->(void)const{return &(static_cast<const node_t*>(current)->data);}
|
||||
constexpr list_iterator& operator++(void){
|
||||
current = current->next;
|
||||
return *this;
|
||||
}
|
||||
constexpr list_iterator operator++(int){
|
||||
list_iterator copy(*this);
|
||||
++(*this);
|
||||
return copy;
|
||||
}
|
||||
constexpr list_iterator& operator--(void){
|
||||
current = current->prev;
|
||||
return *this;
|
||||
}
|
||||
constexpr list_iterator operator--(int){
|
||||
list_iterator copy(*this);
|
||||
--(*this);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr operator const_list_iterator<T>(void)const{
|
||||
return const_list_iterator<T>{current};
|
||||
}
|
||||
|
||||
list_iterator next(void)const{
|
||||
return list_iterator{current->next};
|
||||
}
|
||||
list_iterator prev(void)const{
|
||||
return list_iterator{current->prev};
|
||||
}
|
||||
|
||||
list_node_base* nod(void){return current;}
|
||||
list_node_base* nod(void)const{return current;}
|
||||
};
|
||||
template<class T>
|
||||
struct const_list_iterator{
|
||||
template<class U, class Alloc>
|
||||
friend class rexy::list;
|
||||
|
||||
using value_type = T;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
|
||||
using node_t = list_node<T>;
|
||||
|
||||
const list_node_base* current = nullptr;
|
||||
|
||||
constexpr bool operator==(const const_list_iterator& other)const noexcept{return current == other.current;}
|
||||
constexpr bool operator!=(const const_list_iterator& other)const noexcept{return current != other.current;}
|
||||
constexpr bool operator==(const list_iterator<T>& other)const noexcept{return current == other.nod();}
|
||||
constexpr bool operator!=(const list_iterator<T>& other)const noexcept{return current != other.nod();}
|
||||
|
||||
constexpr const_reference operator*(void){return static_cast<const node_t*>(current)->data;}
|
||||
constexpr const_reference operator*(void)const{return static_cast<const node_t*>(current)->data;}
|
||||
constexpr const_pointer operator->(void){return &(static_cast<const node_t*>(current)->data);}
|
||||
constexpr const_pointer operator->(void)const{return &(static_cast<const node_t*>(current)->data);}
|
||||
constexpr const_list_iterator& operator++(void){
|
||||
current = current->next;
|
||||
return *this;
|
||||
}
|
||||
constexpr const_list_iterator operator++(int){
|
||||
const_list_iterator copy(*this);
|
||||
++(*this);
|
||||
return copy;
|
||||
}
|
||||
constexpr const_list_iterator& operator--(void){
|
||||
current = current->prev;
|
||||
return *this;
|
||||
}
|
||||
constexpr const_list_iterator operator--(int){
|
||||
const_list_iterator copy(*this);
|
||||
--(*this);
|
||||
return copy;
|
||||
}
|
||||
|
||||
const_list_iterator next(void)const{
|
||||
return const_list_iterator{current->next};
|
||||
}
|
||||
const_list_iterator prev(void)const{
|
||||
return const_list_iterator{current->prev};
|
||||
}
|
||||
|
||||
const list_node_base* nod(void)const{return current;}
|
||||
|
||||
protected:
|
||||
list_iterator<T> unconst(void){
|
||||
return list_iterator<T>{const_cast<list_node_base*>(current)};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
namespace detail{
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
constexpr list<T,Alloc>::list(const allocator_type& alloc)noexcept:
|
||||
node_allocator_type(alloc){}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::list(size_type count, const_reference value, const allocator_type& alloc)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>):
|
||||
node_allocator_type(alloc)
|
||||
{
|
||||
constant_initialize_(count, value);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::list(size_type count, const allocator_type& alloc)noexcept(
|
||||
std::is_nothrow_default_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>):
|
||||
node_allocator_type(alloc)
|
||||
{
|
||||
default_initialize_(count);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::list(InputIt first, InputIt last, const allocator_type& alloc)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>):
|
||||
node_allocator_type(alloc)
|
||||
{
|
||||
iterator_initialize_(first, last);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::list(const list& other, const allocator_type& alloc)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>):
|
||||
node_allocator_type(alloc)
|
||||
{
|
||||
iterator_initialize_(other.begin(), other.end());
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::list(list&& other, const allocator_type& alloc)noexcept:
|
||||
node_allocator_type(alloc)
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::list(std::initializer_list<value_type> l, const allocator_type& alloc)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>):
|
||||
node_allocator_type(alloc)
|
||||
{
|
||||
iterator_initialize_(l.begin(), l.end());
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
list<T,Alloc>::~list(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::operator=(const list& other)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> list&
|
||||
{
|
||||
return (*this = list(other));
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::operator=(list&& other)noexcept -> list&{
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::operator=(std::initializer_list<value_type> l)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> list&
|
||||
{
|
||||
return (*this = list(l));
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::operator==(const list& other)const noexcept{
|
||||
if(m_size != other.m_size)
|
||||
return false;
|
||||
return std::equal(cbegin(), cend(), other.cbegin());
|
||||
}
|
||||
#if __cpp_impl_three_way_comparison
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::operator<=>(const list& other)const noexcept{
|
||||
return std::lexicographical_compare_three_way(cbegin(), cend(), other.cbegin(), other.cend());
|
||||
}
|
||||
#else
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::operator!=(const list& other)const noexcept{
|
||||
return !(*this == other);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::operator<(const list& other)const noexcept{
|
||||
return std::lexicographical_compare(cbegin(), cend(), other.cbegin(), other.cend());
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::operator<=(const list& other)const noexcept{
|
||||
return !(other < *this);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::operator>(const list& other)const noexcept{
|
||||
return other < *this;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::operator>=(const list& other)const noexcept{
|
||||
return !(*this < other);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::assign(size_type count, const_reference value)noexcept(
|
||||
std::conditional_t<
|
||||
std::is_copy_assignable_v<value_type>,
|
||||
std::is_nothrow_copy_assignable<value_type>,
|
||||
std::true_type>::value &&
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
assign(sized_constant_iterator{value, count}, sized_constant_iterator<value_type>{{}, 0});
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::assign(InputIt first, InputIt last)noexcept(
|
||||
std::conditional_t<
|
||||
std::is_assignable_v<value_type, decltype(*first)>,
|
||||
std::is_nothrow_assignable<value_type, decltype(*first)>,
|
||||
std::true_type>::value &&
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
auto current = begin();
|
||||
size_type i = 0;
|
||||
|
||||
if constexpr(std::is_assignable_v<value_type, decltype(*first)>){
|
||||
for(;i < m_size && first != last;++i){
|
||||
*current++ = *first++;
|
||||
}
|
||||
if(first == last){
|
||||
erase(current, end());
|
||||
}
|
||||
m_size = i;
|
||||
}else{
|
||||
clear();
|
||||
current = end();
|
||||
}
|
||||
|
||||
while(first != last){
|
||||
current = ++(emplace(current, *first++));
|
||||
}
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::assign(std::initializer_list<value_type> l)noexcept(
|
||||
std::conditional_t<
|
||||
std::is_assignable_v<value_type, decltype(*l.begin())>,
|
||||
std::is_nothrow_assignable<value_type, decltype(*l.begin())>,
|
||||
std::true_type>::value &&
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
assign(l.begin(), l.end());
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::get_allocator(void)const noexcept -> allocator_type{return {*this};}
|
||||
|
||||
//Direct accessors
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::front(void)noexcept -> reference{return *begin();}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::front(void)const noexcept -> const_reference{return *begin();}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::back(void)noexcept -> reference{return *(--end());}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::back(void)const noexcept-> const_reference{return *(--end());}
|
||||
|
||||
//Iterators
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::begin(void)noexcept -> iterator{return iterator{m_sentinel.next};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::begin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::cbegin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::end(void)noexcept -> iterator{return iterator{&m_sentinel};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::end(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::cend(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::next(iterator i)noexcept -> iterator{return i.next();}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::next(const_iterator i)noexcept -> const_iterator{return i.next();}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::prev(iterator i)noexcept -> iterator{return i.prev();}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::prev(const_iterator i)noexcept -> const_iterator{return i.prev();}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::rbegin(void)noexcept -> reverse_iterator{return reverse_iterator{end()};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::rbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::crbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::rend(void)noexcept -> reverse_iterator{return reverse_iterator{begin()};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::rend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::crend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};}
|
||||
|
||||
//Queries
|
||||
template<class T, class Alloc>
|
||||
constexpr bool list<T,Alloc>::empty(void)const noexcept{return m_sentinel.next == nullptr;}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::size(void)const noexcept -> size_type{return m_size;}
|
||||
template<class T, class Alloc>
|
||||
constexpr auto list<T,Alloc>::max_size(void)const noexcept -> size_type{return std::numeric_limits<difference_type>::max();}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::clear(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
erase(begin(), end());
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::insert(const_iterator pos, const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
return insert(pos, value_type{value});
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::insert(const_iterator pos, value_type&& value)noexcept(
|
||||
std::is_nothrow_move_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
auto* prev = (--pos).unconst().nod();
|
||||
auto* next = prev->next;
|
||||
prev->next = this->allocate(1);
|
||||
next->prev = prev->next;
|
||||
std::construct_at(static_cast<node*>(prev->next), next, prev, std::move(value));
|
||||
++m_size;
|
||||
return iterator{prev->next};
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::insert(const_iterator pos, size_type count, const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
auto start = pos.unconst();
|
||||
for(auto i = count;i > 0;--i){
|
||||
pos = ++(insert(pos, value_type{value}));
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::insert(const_iterator pos, InputIt first, InputIt last)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
auto start = pos.unconst();
|
||||
while(first != last){
|
||||
pos = ++(insert(pos, *first++));
|
||||
}
|
||||
return start;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::insert(const_iterator pos, std::initializer_list<value_type> l)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
return insert(pos, l.begin(), l.end());
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
template<class... Args>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::emplace(const_iterator pos, Args&&... args)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, Args&&...> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
auto* prev = (--pos).unconst().nod();
|
||||
auto* next = prev->next;
|
||||
prev->next = this->allocate(1);
|
||||
next->prev = prev->next;
|
||||
std::construct_at(static_cast<node*>(prev->next), next, prev, std::forward<Args>(args)...);
|
||||
++m_size;
|
||||
return iterator{prev->next};
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::erase(const_iterator pos)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
auto* n = pos.unconst().nod();
|
||||
auto* next = n->next;
|
||||
|
||||
remove_node_(n);
|
||||
std::destroy_at(static_cast<node*>(n));
|
||||
this->deallocate(static_cast<node*>(n), 1);
|
||||
--m_size;
|
||||
return iterator{next};
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::erase(const_iterator first, const_iterator last)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> iterator
|
||||
{
|
||||
while(first != last){
|
||||
first = erase(first);
|
||||
}
|
||||
return last.unconst();
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::push_back(const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> reference
|
||||
{
|
||||
return *insert(cend(), value);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::push_back(value_type&& value)noexcept(
|
||||
std::is_nothrow_move_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> reference
|
||||
{
|
||||
return *insert(cend(), std::move(value));
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class... Args>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::emplace_back(Args&&... args)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, Args&&...> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> reference
|
||||
{
|
||||
return *emplace(cend(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::pop_back(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
erase(--cend());
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::push_front(const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> reference
|
||||
{
|
||||
return insert(cbegin(), value);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::push_front(value_type&& value)noexcept(
|
||||
std::is_nothrow_move_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> reference
|
||||
{
|
||||
return insert(cbegin(), std::move(value));
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class... Args>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::emplace_front(Args&&... args)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, Args&&...> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> reference
|
||||
{
|
||||
return emplace(cbegin(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::pop_front(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
erase(cbegin());
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::resize(size_type count)noexcept(
|
||||
std::is_nothrow_default_constructible_v<value_type> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
resize(count, value_type{});
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::resize(size_type count, value_type value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
while(count > m_size){
|
||||
insert(cend(), value);
|
||||
}
|
||||
while(m_size > count){
|
||||
erase(--cend());
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::swap(list& other)noexcept{
|
||||
std::swap(m_sentinel, other.m_sentinel);
|
||||
std::swap(m_size, other.m_size);
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::merge(list&& other)noexcept{
|
||||
merge(std::move(other), [](const_reference a, const_reference b){return a < b;});
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class Comp>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::merge(list&& other, Comp comp)noexcept{
|
||||
if(&other == this){
|
||||
return;
|
||||
}
|
||||
auto it = begin();
|
||||
auto oit = other.begin();
|
||||
|
||||
while(it != end() && oit != other.end()){
|
||||
if(comp(*oit, *it)){
|
||||
oit = iterator{get_next_then_move_node_(it.prev().nod(), oit.nod())};
|
||||
++m_size;
|
||||
}else{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
splice(it, std::move(other));
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::splice(const_iterator pos, list&& other)noexcept{
|
||||
splice(pos, std::move(other), other.begin(), other.end());
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::splice(const_iterator pos, list&& other, const_iterator it)noexcept{
|
||||
auto oit = it.unconst();
|
||||
auto prev = (--pos).unconst();
|
||||
remove_node_(oit.nod());
|
||||
insert_node_(prev.nod(), oit.nod());
|
||||
|
||||
++m_size;
|
||||
--other.m_size;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::splice(const_iterator pos, list&& other, const_iterator first, const_iterator last)noexcept{
|
||||
for(auto oit = first;oit != last;){
|
||||
auto onext = oit.next();
|
||||
splice(pos, std::move(other), oit);
|
||||
oit = onext;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::remove(const_reference value)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> size_type
|
||||
{
|
||||
return remove_if(
|
||||
[&value](const_reference r) -> bool{
|
||||
return r == value;
|
||||
}
|
||||
);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class UnaryPred>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::remove_if(UnaryPred pred)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> size_type
|
||||
{
|
||||
size_type count = 0;
|
||||
for(auto it = begin();it != end();){
|
||||
if(pred(*it)){
|
||||
it = erase(it);
|
||||
++count;
|
||||
}else{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::reverse(void)noexcept{
|
||||
auto* it = begin().nod();
|
||||
std::swap(m_sentinel.next, m_sentinel.prev);
|
||||
|
||||
for(;it != &m_sentinel;it = it->prev){
|
||||
std::swap(it->next, it->prev);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::unique(void)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> size_type
|
||||
{
|
||||
return unique(
|
||||
[](const_reference first, const_reference second) -> bool{
|
||||
return first == second;
|
||||
}
|
||||
);
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class BinaryPred>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto list<T,Alloc>::unique(BinaryPred pred)noexcept(
|
||||
std::is_nothrow_destructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>) -> size_type
|
||||
{
|
||||
size_type count = 0;
|
||||
for(auto it = begin();it != end();++it){
|
||||
auto next = it.next();
|
||||
while(next != end() && pred(*it, *next)){
|
||||
next = erase(next);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::sort(void)noexcept{
|
||||
mergesort(begin(), m_size, [](const_reference first, const_reference second)->bool{
|
||||
return first < second;
|
||||
});
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
template<class Comp>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::sort(Comp comp)noexcept{
|
||||
mergesort(begin(), m_size, comp);
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::iterator_initialize_(InputIt first, InputIt last)noexcept(
|
||||
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
for(;first != last;++first){
|
||||
emplace_back(*first);
|
||||
}
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::constant_initialize_(size_type count, const_reference value)noexcept(
|
||||
std::is_nothrow_copy_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
for(;count > 0;--count){
|
||||
emplace_back(value);
|
||||
}
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void list<T,Alloc>::default_initialize_(size_type count)noexcept(
|
||||
std::is_nothrow_default_constructible_v<value_type> &&
|
||||
is_nothrow_allocator_v<node_allocator_type>)
|
||||
{
|
||||
for(;count > 0;--count){
|
||||
emplace_back();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
template<class Comp>
|
||||
auto list<T,Alloc>::mergesort(iterator first, size_type firstlen, Comp comp)noexcept -> iterator{
|
||||
if(firstlen == 1)
|
||||
return first;
|
||||
auto [second, secondlen] = ms_split(first, firstlen);
|
||||
firstlen -= secondlen;
|
||||
|
||||
first = mergesort(first, firstlen, comp);
|
||||
second = mergesort(second, secondlen, comp);
|
||||
|
||||
iterator result = first;
|
||||
|
||||
//do this outside the loop to save return value
|
||||
if(comp(*second, *first)){
|
||||
result = second;
|
||||
second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())};
|
||||
--secondlen;
|
||||
}
|
||||
|
||||
while(firstlen > 0 && secondlen > 0){
|
||||
if(comp(*second, *first)){
|
||||
second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())};
|
||||
--secondlen;
|
||||
}else{
|
||||
++first;
|
||||
--firstlen;
|
||||
}
|
||||
}
|
||||
//finish off the second list if it isn't already done
|
||||
while(secondlen > 0){
|
||||
second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())};
|
||||
--secondlen;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
auto list<T,Alloc>::ms_split(iterator it, size_type len)noexcept -> std::pair<iterator,size_type>{
|
||||
size_type second_half_len = len / 2;
|
||||
size_type dist = len - second_half_len;
|
||||
for(auto i = dist;i > 0;--i){
|
||||
++it;
|
||||
}
|
||||
return {it, second_half_len};
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
void list<T,Alloc>::insert_node_(detail::list_node_base* prev, detail::list_node_base* n)noexcept{
|
||||
n->next = prev->next;
|
||||
n->prev = prev;
|
||||
prev->next->prev = n;
|
||||
prev->next = n;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
void list<T,Alloc>::remove_node_(detail::list_node_base* rm)noexcept{
|
||||
rm->prev->next = rm->next;
|
||||
rm->next->prev = rm->prev;
|
||||
}
|
||||
template<class T, class Alloc>
|
||||
detail::list_node_base* list<T,Alloc>::get_next_then_move_node_(detail::list_node_base* dest, detail::list_node_base* n)noexcept{
|
||||
auto* next = n->next;
|
||||
remove_node_(n);
|
||||
insert_node_(dest, n);
|
||||
return next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -20,10 +20,11 @@
|
||||
#define REXY_MPMC_QUEUE_HPP
|
||||
|
||||
#include <vector> //vector (duh)
|
||||
#include <cstdlib> //size_t
|
||||
#include <cstddef> //size_t
|
||||
#include <atomic> //atomic (duh)
|
||||
|
||||
#include "rexy.hpp"
|
||||
#include "utility.hpp" //min
|
||||
|
||||
#ifdef __cpp_lib_hardware_interference_size
|
||||
#include <new> //hardware_destructive_interference_size
|
||||
@ -42,7 +43,7 @@ namespace rexy{
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using size_type = std::size_t;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
@ -53,14 +54,14 @@ namespace rexy{
|
||||
#if defined(__cpp_lib_hardware_interference_size)
|
||||
//libc++ bug
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=41423
|
||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 11000
|
||||
static constexpr size_t cacheline_size = std::hardware_destructive_interference_size;
|
||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 12000
|
||||
static constexpr std::size_t cacheline_size = std::hardware_destructive_interference_size;
|
||||
#else
|
||||
static constexpr size_t cacheline_size = 64;
|
||||
static constexpr std::size_t cacheline_size = 64;
|
||||
#endif
|
||||
#else
|
||||
//Best guess
|
||||
static constexpr size_t cacheline_size = 64;
|
||||
static constexpr std::size_t cacheline_size = 64;
|
||||
#endif
|
||||
|
||||
class slot
|
||||
@ -72,7 +73,7 @@ namespace rexy{
|
||||
alignas(cacheline_size) std::atomic<size_type> m_turn = {0};
|
||||
alignas(alignof(value_type)) unsigned char m_data[sizeof(value_type)] = {};
|
||||
//ensure no false sharing with following data
|
||||
char cachline_padding[cacheline_size - (sizeof(m_data) + sizeof(m_turn))];
|
||||
char cachline_padding[cacheline_size - ((sizeof(m_data) + sizeof(m_turn)) % cacheline_size)];
|
||||
public:
|
||||
slot() = default;
|
||||
slot(const slot& s);
|
||||
@ -121,6 +122,8 @@ namespace rexy{
|
||||
|
||||
void pop(reference t);
|
||||
bool try_pop(reference t);
|
||||
|
||||
size_type size(void)const;
|
||||
private:
|
||||
constexpr size_type rotation_cnt(size_type t);
|
||||
};
|
||||
|
||||
@ -21,20 +21,20 @@
|
||||
|
||||
#include <utility> //forward, move
|
||||
#include <atomic> //memory_order, atomic
|
||||
#include <cstring> //memcpy
|
||||
#include "utility.hpp" //memcpy
|
||||
|
||||
namespace rexy{
|
||||
template<class T>
|
||||
mpmc_queue<T>::slot::slot(const slot& s):
|
||||
m_turn(s.m_turn.load(std::memory_order_acquire))
|
||||
{
|
||||
memcpy(m_data, s.m_data, sizeof(s.m_data));
|
||||
rexy::memcpy(m_data, s.m_data, sizeof(s.m_data));
|
||||
}
|
||||
template<class T>
|
||||
mpmc_queue<T>::slot::slot(slot&& s):
|
||||
m_turn(s.m_turn.load(std::memory_order_acquire))
|
||||
{
|
||||
memcpy(m_data, s.m_data, sizeof(s.m_data));
|
||||
rexy::memcpy(m_data, s.m_data, sizeof(s.m_data));
|
||||
}
|
||||
template<class T>
|
||||
mpmc_queue<T>::slot::~slot(){
|
||||
@ -128,12 +128,13 @@ namespace rexy{
|
||||
void mpmc_queue<T>::emplace(Args&&... args){
|
||||
const size_type head = m_head.fetch_add(1, std::memory_order_seq_cst);
|
||||
slot& s = m_slots[head % m_slots.capacity()];
|
||||
const size_type rot_count = rotation_cnt(head);
|
||||
//lsb is in-use flag. wait for it to be 0
|
||||
while(rotation_cnt(head) << 1 != s.turn().load(std::memory_order_acquire));
|
||||
while(rot_count << 1 != s.turn().load(std::memory_order_acquire));
|
||||
|
||||
s.construct(std::forward<Args>(args)...);
|
||||
//set in-use flag
|
||||
s.turn().store((rotation_cnt(head) << 1) + 1, std::memory_order_release);
|
||||
s.turn().store((rot_count << 1) + 1, std::memory_order_release);
|
||||
}
|
||||
template<class T>
|
||||
template<class... Args>
|
||||
@ -208,6 +209,10 @@ namespace rexy{
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
auto mpmc_queue<T>::size(void)const -> size_type{
|
||||
return m_slots.size();
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto mpmc_queue<T>::rotation_cnt(size_type t) -> size_type{
|
||||
return (t / m_slots.capacity());
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2021 rexy712
|
||||
Copyright (C) 2021-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,9 +16,20 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_REXY_HPP
|
||||
#define REXY_REXY_HPP
|
||||
#ifndef REXY_CONFIG_REXY_HPP
|
||||
#define REXY_CONFIG_REXY_HPP
|
||||
|
||||
#define LIBREXY_BUILD_HEADER_ONLY @librexy_HEADER_ONLY_BUILD@
|
||||
|
||||
#if LIBREXY_BUILD_HEADER_ONLY
|
||||
#define LIBREXY_HEADER_ONLY 1
|
||||
#endif
|
||||
|
||||
#define LIBREXY_VERSION @librexy_VERSION_STRING@
|
||||
#define LIBREXY_VERSION_MAJOR @librexy_VERSION_MAJOR@
|
||||
#define LIBREXY_VERSION_MINOR @librexy_VERSION_MINOR@
|
||||
#define LIBREXY_VERSION_REVISION @librexy_VERSION_REVISION@
|
||||
|
||||
#define LIBREXY_ENABLE_SSO @librexy_ENABLE_SSO@
|
||||
|
||||
#endif
|
||||
|
||||
@ -34,7 +34,7 @@ namespace rexy{
|
||||
|
||||
public:
|
||||
template<class U>
|
||||
constexpr steal(U&& u)
|
||||
constexpr explicit steal(U&& u)
|
||||
noexcept(std::is_nothrow_constructible<T,U&&>::value):
|
||||
m_val(std::forward<U>(u)){}
|
||||
|
||||
|
||||
97
include/rexy/storage_for.hpp
Normal file
97
include/rexy/storage_for.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_STORAGE_FOR_HPP
|
||||
#define REXY_STORAGE_FOR_HPP
|
||||
|
||||
#include <utility> //forward, move
|
||||
#include <type_traits> //is_nothrow_constructible, etc
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
//A wrapper around raw storage.
|
||||
//Allows default constructing objects with members which cannot be default constructed
|
||||
|
||||
template<class T>
|
||||
class storage_for
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using rvalue_reference = T&&;
|
||||
|
||||
static constexpr auto align = alignof(value_type);
|
||||
static constexpr auto size = sizeof(value_type);
|
||||
|
||||
private:
|
||||
#if __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) && __cpp_constexpr >= 202002L
|
||||
|
||||
union storage_{
|
||||
[[no_unique_address]]
|
||||
struct empty_{}empty = {};
|
||||
T data;
|
||||
}m_storage;
|
||||
|
||||
#else //__cplusplus
|
||||
|
||||
alignas(align) unsigned char m_storage[size];
|
||||
|
||||
#endif //__cplusplus
|
||||
unsigned char m_dirty:1 = 0;
|
||||
|
||||
public:
|
||||
constexpr storage_for(void) = default;
|
||||
template<class... Args>
|
||||
constexpr storage_for(Args&&... args)noexcept(std::is_nothrow_constructible<value_type,Args...>::value);
|
||||
constexpr storage_for(const_reference t)noexcept(std::is_nothrow_copy_constructible<value_type>::value);
|
||||
constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value);
|
||||
constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value);
|
||||
constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value);
|
||||
REXY_CPP20_CONSTEXPR ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value);
|
||||
|
||||
constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value);
|
||||
constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value);
|
||||
constexpr storage_for& operator=(const_reference t)noexcept(std::is_nothrow_copy_assignable<value_type>::value);
|
||||
constexpr storage_for& operator=(rvalue_reference t)noexcept(std::is_nothrow_move_assignable<value_type>::value);
|
||||
constexpr void destroy(void)noexcept(std::is_nothrow_destructible<value_type>::value);
|
||||
|
||||
constexpr operator reference(void)noexcept;
|
||||
constexpr operator const_reference(void)const noexcept;
|
||||
constexpr operator rvalue_reference(void)&& noexcept;
|
||||
|
||||
constexpr reference get(void)noexcept;
|
||||
constexpr const_reference get(void)const noexcept;
|
||||
|
||||
constexpr pointer operator->(void)noexcept;
|
||||
constexpr const_pointer operator->(void)const noexcept;
|
||||
constexpr reference operator*(void)noexcept;
|
||||
constexpr const_reference operator*(void)const noexcept;
|
||||
|
||||
constexpr bool valid(void)const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "storage_for.tpp"
|
||||
|
||||
#endif
|
||||
269
include/rexy/storage_for.tpp
Normal file
269
include/rexy/storage_for.tpp
Normal file
@ -0,0 +1,269 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_STORAGE_FOR_TPP
|
||||
#define REXY_STORAGE_FOR_TPP
|
||||
|
||||
#include <utility> //forward, move
|
||||
|
||||
#if __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) && __cpp_constexpr >= 202002L
|
||||
|
||||
#include <memory> //construct_at, destroy_at
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T>
|
||||
template<class... Args>
|
||||
constexpr storage_for<T>::storage_for(Args&&... args)noexcept(std::is_nothrow_constructible<value_type,Args...>::value){
|
||||
std::construct_at(&m_storage.data, std::forward<Args>(args)...);
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(const_reference t)noexcept(std::is_nothrow_copy_constructible<value_type>::value){
|
||||
std::construct_at(&m_storage.data, t);
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value){
|
||||
std::construct_at(&m_storage.data, std::move(t));
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value){
|
||||
if(s.m_dirty){
|
||||
std::construct_at(&m_storage.data, s.m_storage.data);
|
||||
m_dirty = 1;
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value){
|
||||
if(s.m_dirty){
|
||||
std::construct_at(&m_storage.data, std::move(s.m_storage.data));
|
||||
m_dirty = 1;
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
REXY_CPP20_CONSTEXPR storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){
|
||||
if(m_dirty){
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value){
|
||||
return ((*this) = storage_for(s));
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value){
|
||||
if(s.m_dirty){
|
||||
return ((*this) = std::move(s.m_storage.data));
|
||||
}else{
|
||||
if(m_dirty){
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(const_reference t)noexcept(std::is_nothrow_copy_assignable<value_type>::value){
|
||||
if(m_dirty){
|
||||
m_storage.data = t;
|
||||
}else{
|
||||
std::construct_at(&m_storage.data, t);
|
||||
}
|
||||
m_dirty = 1;
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(rvalue_reference t)noexcept(std::is_nothrow_move_assignable<value_type>::value){
|
||||
if(m_dirty){
|
||||
m_storage.data = std::move(t);
|
||||
}else{
|
||||
std::construct_at(&m_storage.data, std::move(t));
|
||||
}
|
||||
m_dirty = 1;
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr void storage_for<T>::destroy(void)noexcept(std::is_nothrow_destructible<value_type>::value){
|
||||
if(m_dirty){
|
||||
std::destroy_at(&m_storage.data);
|
||||
m_dirty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator reference(void)noexcept{
|
||||
return m_storage.data;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator const_reference(void)const noexcept{
|
||||
return m_storage.data;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
|
||||
return std::move(m_storage.data);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::get(void)noexcept -> reference{
|
||||
return m_storage.data;
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::get(void)const noexcept -> const_reference{
|
||||
return m_storage.data;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
|
||||
return &m_storage.data;
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator->(void)const noexcept -> const_pointer{
|
||||
return &m_storage.data;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator*(void)noexcept -> reference{
|
||||
return m_storage.data;
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator*(void)const noexcept -> const_reference{
|
||||
return m_storage.data;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr bool storage_for<T>::valid(void)const{
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else //__cplusplus
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class T>
|
||||
template<class... Args>
|
||||
constexpr storage_for<T>::storage_for(Args&&... args)noexcept(std::is_nothrow_constructible<value_type,Args...>::value){
|
||||
new (m_storage) T(std::forward<Args>(args)...);
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(const_reference t)noexcept(std::is_nothrow_copy_constructible<value_type>::value){
|
||||
new (m_storage) T(t);
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value){
|
||||
new (m_storage) T(std::move(t));
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value){
|
||||
new (m_storage) T(reinterpret_cast<const T&>(s.m_storage));
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value){
|
||||
new (m_storage) T(std::move(reinterpret_cast<T&>(&s.m_storage)));
|
||||
m_dirty = 1;
|
||||
}
|
||||
template<class T>
|
||||
REXY_CPP20_CONSTEXPR storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){
|
||||
if(m_dirty){
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value){
|
||||
reinterpret_cast<T&>(m_storage) = static_cast<const T&>(s);
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value){
|
||||
reinterpret_cast<T&>(m_storage) = static_cast<T&&>(std::move(s));
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(const_reference t)noexcept(std::is_nothrow_copy_assignable<value_type>::value){
|
||||
reinterpret_cast<T&>(m_storage) = t;
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>& storage_for<T>::operator=(rvalue_reference t)noexcept(std::is_nothrow_move_assignable<value_type>::value){
|
||||
reinterpret_cast<T&>(m_storage) = std::move(t);
|
||||
return *this;
|
||||
}
|
||||
template<class T>
|
||||
constexpr void storage_for<T>::destroy(void)noexcept(std::is_nothrow_destructible<value_type>::value){
|
||||
reinterpret_cast<T*>(&m_storage)->~T();
|
||||
m_dirty = 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator reference(void)noexcept{
|
||||
return reinterpret_cast<reference>(m_storage);
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator const_reference(void)const noexcept{
|
||||
return reinterpret_cast<const_reference>(m_storage);
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
|
||||
return reinterpret_cast<rvalue_reference>(std::move(m_storage));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::get(void)noexcept -> reference{
|
||||
return reinterpret_cast<reference>(m_storage);
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::get(void)const noexcept -> const_reference{
|
||||
return reinterpret_cast<const_reference>(m_storage);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
|
||||
return reinterpret_cast<T*>(&m_storage);
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator->(void)const noexcept -> const_pointer{
|
||||
return reinterpret_cast<const T*>(&m_storage);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator*(void)noexcept -> reference{
|
||||
return reinterpret_cast<T&>(m_storage);
|
||||
}
|
||||
template<class T>
|
||||
constexpr auto storage_for<T>::operator*(void)const noexcept -> const_reference{
|
||||
return reinterpret_cast<const T&>(m_storage);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr bool storage_for<T>::valid(void)const{
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
@ -26,17 +26,25 @@
|
||||
namespace rexy{
|
||||
|
||||
//new allocated string
|
||||
using string = basic_string<char,allocator<char>>;
|
||||
using string = basic_string<char>;
|
||||
using wstring = basic_string<wchar_t>;
|
||||
using u16string = basic_string<char16_t>;
|
||||
using u32string = basic_string<char32_t>;
|
||||
#ifdef __cpp_char8_t
|
||||
using u8string = basic_string<char8_t>;
|
||||
#endif
|
||||
|
||||
#ifndef LIBREXY_HEADER_ONLY
|
||||
extern template class basic_string<char,allocator<char>>;
|
||||
extern template class basic_string<wchar_t,allocator<wchar_t>>;
|
||||
extern template class basic_string<char16_t,allocator<char16_t>>;
|
||||
extern template class basic_string<char32_t,allocator<char32_t>>;
|
||||
#ifdef __cpp_char8_t
|
||||
extern template class basic_string<char8_t,allocator<char8_t>>;
|
||||
#endif
|
||||
|
||||
#endif //LIBREXY_HEADER_ONLY
|
||||
|
||||
}
|
||||
|
||||
#ifdef REXY_CX_HASH_HPP
|
||||
#include "cx/basic_string_hash.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -21,28 +21,38 @@
|
||||
|
||||
#include <type_traits> //is_same, integral_contant, enable_if, etc
|
||||
#include <utility> //forward
|
||||
#include <cstddef> //size_t,ptrdiff
|
||||
#include <cstring> //strlen
|
||||
#include <cstddef> //std::size_t,ptrdiff
|
||||
#include <climits> //CHAR_BIT
|
||||
#include <iterator> //reverse_iterator
|
||||
#include <initializer_list>
|
||||
|
||||
#include "steal.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "utility.hpp" //memcpy
|
||||
#include "traits.hpp"
|
||||
#include "expression.hpp"
|
||||
#include "detail/string_appender.hpp"
|
||||
#include "detail/hasallocator.hpp"
|
||||
#include "allocator.hpp"
|
||||
#include "rexy.hpp"
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
#ifndef LIBREXY_ENABLE_SSO
|
||||
#define LIBREXY_ENABLE_SSO 1
|
||||
#endif
|
||||
|
||||
template<class Char>
|
||||
class basic_string_view;
|
||||
|
||||
//Base of all RAII strings. Its use is allowing passing of rexy strings to functions without knowing the exact type
|
||||
template<class Char>
|
||||
class string_base
|
||||
{
|
||||
public:
|
||||
using value_type = Char;
|
||||
using size_type = size_t;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
@ -53,6 +63,7 @@ namespace rexy{
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
#if LIBREXY_ENABLE_SSO != 0
|
||||
private:
|
||||
static constexpr size_type EXTRA_SDATA_LEN = 0;
|
||||
|
||||
@ -62,7 +73,7 @@ namespace rexy{
|
||||
size_type capacity:(CHAR_BIT*sizeof(size_type)-1); //take away last bit from capacity for islong
|
||||
size_type length; //length of string excluding null terminator
|
||||
constexpr ldata(void)noexcept:
|
||||
islong(0),
|
||||
islong(1),
|
||||
capacity(0),
|
||||
length(0){}
|
||||
};
|
||||
@ -70,7 +81,7 @@ namespace rexy{
|
||||
static constexpr size_type MAX_SHORT_LEN = EXTRA_SDATA_LEN+sizeof(ldata)-2;
|
||||
//represent short string
|
||||
struct sdata{
|
||||
unsigned char islong:1; //common subsequenci with long string
|
||||
unsigned char islong:1; //common subsequence with long string
|
||||
unsigned char length:(CHAR_BIT-1); //take away last bit from length for islong, excludes null terminator
|
||||
value_type data[MAX_SHORT_LEN+1]; //char array for string storage
|
||||
constexpr sdata(void)noexcept:
|
||||
@ -78,12 +89,12 @@ namespace rexy{
|
||||
length(0),
|
||||
data{}{}
|
||||
};
|
||||
//union of short and long string representations. Default to long representation for static_string's use case.
|
||||
//union of short and long string representations.
|
||||
union combine_data{
|
||||
ldata l;
|
||||
sdata s;
|
||||
constexpr combine_data(void)noexcept:
|
||||
l(){}
|
||||
s(){}
|
||||
}m_data;
|
||||
|
||||
//direct access to current string data regardless of representation. Increases access speed.
|
||||
@ -124,6 +135,12 @@ namespace rexy{
|
||||
constexpr void set_short_capacity(size_type){}
|
||||
constexpr size_type get_long_capacity(void)const{return m_data.l.capacity;}
|
||||
constexpr size_type get_short_capacity(void)const{return MAX_SHORT_LEN;}
|
||||
constexpr void set_capacity(size_type s){
|
||||
if(islong())
|
||||
set_long_capacity(s);
|
||||
else
|
||||
set_short_capacity(s);
|
||||
}
|
||||
constexpr void set_length(size_type s){
|
||||
if(islong())
|
||||
set_long_length(s);
|
||||
@ -143,8 +160,7 @@ namespace rexy{
|
||||
}else if(len){
|
||||
set_islong_flag(false);
|
||||
pointer raw = set_short_ptr();
|
||||
if(len)
|
||||
memcpy(raw, data, sizeof(value_type)*len);
|
||||
rexy::memcpy(raw, data, sizeof(value_type)*len);
|
||||
raw[len] = 0;
|
||||
set_short_length(len);
|
||||
set_short_capacity(cap);
|
||||
@ -161,8 +177,7 @@ namespace rexy{
|
||||
{
|
||||
s.set_islong_flag(false);
|
||||
}
|
||||
~string_base(void)noexcept = default;
|
||||
|
||||
REXY_CPP20_CONSTEXPR ~string_base(void)noexcept = default;
|
||||
constexpr string_base& operator=(string_base&& s)noexcept{
|
||||
std::swap(m_data, s.m_data);
|
||||
if(this->islong())
|
||||
@ -173,9 +188,7 @@ namespace rexy{
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
//Length of string not including null terminator
|
||||
constexpr size_type length(void)const noexcept{
|
||||
if(islong())
|
||||
return get_long_length();
|
||||
@ -188,56 +201,192 @@ namespace rexy{
|
||||
else
|
||||
return get_short_capacity();
|
||||
}
|
||||
static constexpr bool uses_sso(void){return true;}
|
||||
static constexpr size_type short_string_size(void){return MAX_SHORT_LEN;}
|
||||
#else //LIBREXY_ENABLE_SSO
|
||||
private:
|
||||
//direct access to current string
|
||||
pointer m_raw = nullptr;
|
||||
size_type m_length = 0;
|
||||
size_type m_capacity = 0;
|
||||
|
||||
protected:
|
||||
constexpr pointer get_pointer(void){return m_raw;}
|
||||
constexpr const_pointer get_pointer(void)const{return m_raw;}
|
||||
constexpr void set_length(size_type s){m_length = s;}
|
||||
constexpr void set_capacity(size_type s){m_capacity = s;}
|
||||
constexpr bool islong(void)const{return true;}
|
||||
constexpr pointer set_short_ptr(void){return m_raw;}
|
||||
constexpr pointer set_long_ptr(pointer ptr){return m_raw = ptr;}
|
||||
|
||||
protected:
|
||||
constexpr string_base(void)noexcept = default;
|
||||
//Initialize without copying
|
||||
constexpr string_base(pointer data, size_type len, size_type cap)noexcept:
|
||||
m_raw(data),
|
||||
m_length(len),
|
||||
m_capacity(cap){}
|
||||
constexpr string_base(pointer data, size_type len)noexcept:
|
||||
string_base(data, len, len){}
|
||||
//Copy ctor, copy length+capacity+short string, not long string value
|
||||
constexpr string_base(const string_base& s)noexcept:
|
||||
m_raw(s.m_raw),
|
||||
m_length(s.m_length),
|
||||
m_capacity(s.m_capacity){}
|
||||
constexpr string_base(string_base&& s)noexcept:
|
||||
m_raw(std::exchange(s.m_raw, nullptr)),
|
||||
m_length(s.m_length),
|
||||
m_capacity(s.m_capacity){}
|
||||
REXY_CPP20_CONSTEXPR ~string_base(void)noexcept = default;
|
||||
constexpr string_base& operator=(string_base&& s)noexcept{
|
||||
std::swap(m_raw, s.m_raw);
|
||||
m_length = s.m_length;
|
||||
m_capacity = s.m_capacity;
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
constexpr size_type length(void)const noexcept{
|
||||
return m_length;
|
||||
}
|
||||
constexpr size_type capacity(void)const noexcept{
|
||||
return m_capacity;
|
||||
}
|
||||
static constexpr bool uses_sso(void){return false;}
|
||||
static constexpr size_type short_string_size(void){return 0;}
|
||||
#endif //LIBREXY_ENABLE_SSO
|
||||
public:
|
||||
//Length of string not including null terminator
|
||||
constexpr size_type size(void)const noexcept{
|
||||
return length();
|
||||
}
|
||||
constexpr size_type max_size(void)const noexcept{
|
||||
return size_type{-2};
|
||||
}
|
||||
//direct access to managed pointer
|
||||
constexpr pointer c_str(void)noexcept{return get_pointer();}
|
||||
constexpr const_pointer c_str(void)const noexcept{return get_pointer();}
|
||||
constexpr pointer get(void)noexcept{return get_pointer();}
|
||||
constexpr const_pointer get(void)const noexcept{return get_pointer();}
|
||||
constexpr operator pointer(void)noexcept{return get_pointer();}
|
||||
constexpr operator const_pointer(void)const noexcept{return get_pointer();}
|
||||
constexpr pointer data(void)noexcept{return get_pointer();}
|
||||
constexpr const_pointer data(void)const noexcept{return get_pointer();}
|
||||
constexpr operator basic_string_view<value_type>(void)const noexcept{return basic_string_view<value_type>(begin(), end());}
|
||||
|
||||
//true if m_data is not empty
|
||||
constexpr bool valid(void)const noexcept{return length() > 0;}
|
||||
constexpr bool empty(void)const noexcept{return length() == 0;}
|
||||
|
||||
constexpr reference operator[](size_type i)noexcept{return get_pointer()[i];}
|
||||
constexpr const_reference operator[](size_type i)const noexcept{return get_pointer()[i];}
|
||||
constexpr reference at(size_type i)noexcept{return get_pointer()[i];}
|
||||
constexpr const_reference at(size_type i)const noexcept{return get_pointer()[i];}
|
||||
|
||||
constexpr const_iterator search(const string_base& s)const;
|
||||
constexpr const_iterator search(const_pointer c)const;
|
||||
constexpr iterator search(const string_base& s);
|
||||
constexpr iterator search(const_pointer c);
|
||||
constexpr const_iterator search(basic_string_view<value_type> sv)const noexcept;
|
||||
constexpr iterator search(basic_string_view<value_type> sv)noexcept;
|
||||
constexpr const_iterator search(const_pointer c)const noexcept;
|
||||
constexpr iterator search(const_pointer c)noexcept;
|
||||
template<class Searcher>
|
||||
constexpr const_iterator search(const string_base& s, const Searcher& searcher)const;
|
||||
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>);
|
||||
template<class Searcher>
|
||||
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const;
|
||||
template<class Searcher>
|
||||
constexpr iterator search(const string_base& s, const Searcher& searcher);
|
||||
template<class Searcher>
|
||||
constexpr iterator search(const_pointer c, const Searcher& searcher);
|
||||
constexpr bool compare(const string_base& s)const{return *this == s;}
|
||||
constexpr bool compare(const_pointer c)const{return *this == c;}
|
||||
constexpr iterator search(const_pointer c, const Searcher& searcher)noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, iterator, iterator, const_pointer, const_pointer>);
|
||||
|
||||
constexpr iterator begin(void){return get_pointer();}
|
||||
constexpr const_iterator begin(void)const{return get_pointer();}
|
||||
constexpr iterator end(void){return get_pointer()+length();}
|
||||
constexpr const_iterator end(void)const{return get_pointer()+length();}
|
||||
constexpr const_iterator cbegin(void)const{return begin();}
|
||||
constexpr const_iterator cend(void)const{return end();}
|
||||
constexpr const_iterator rsearch(basic_string_view<value_type> sv)const noexcept;
|
||||
constexpr iterator rsearch(basic_string_view<value_type> sv)noexcept;
|
||||
constexpr const_iterator rsearch(const_pointer c)const noexcept;
|
||||
constexpr iterator rsearch(const_pointer c)noexcept;
|
||||
template<class Searcher>
|
||||
constexpr const_iterator rsearch(const_pointer c, const Searcher& searcher)const noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>);
|
||||
template<class Searcher>
|
||||
constexpr iterator rsearch(const_pointer c, const Searcher& searcher)noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, iterator, iterator, const_pointer, const_pointer>);
|
||||
|
||||
constexpr reverse_iterator rbegin(void){return reverse_iterator(get_pointer()+length());}
|
||||
constexpr const_reverse_iterator rbegin(void)const{return const_reverse_iterator(get_pointer()+length());}
|
||||
constexpr reverse_iterator rend(void){return reverse_iterator(get_pointer()-1);}
|
||||
constexpr const_reverse_iterator rend(void)const{return const_reverse_iterator(get_pointer()-1);}
|
||||
constexpr const_reverse_iterator crbegin(void)const{return rbegin();}
|
||||
constexpr const_reverse_iterator crend(void)const{return rend();}
|
||||
constexpr bool starts_with(basic_string_view<value_type> sv)const noexcept;
|
||||
constexpr bool starts_with(value_type v)const noexcept;
|
||||
constexpr bool starts_with(const_pointer str)const noexcept;
|
||||
|
||||
static constexpr bool uses_sso(void){return true;}
|
||||
static constexpr size_type short_string_size(void){return MAX_SHORT_LEN;}
|
||||
};
|
||||
constexpr bool ends_with(basic_string_view<value_type> sv)const noexcept;
|
||||
constexpr bool ends_with(value_type v)const noexcept;
|
||||
constexpr bool ends_with(const_pointer str)const noexcept;
|
||||
|
||||
constexpr bool contains(basic_string_view<value_type> sv)const noexcept;
|
||||
constexpr bool contains(value_type v)const noexcept;
|
||||
constexpr bool contains(const_pointer str)const noexcept;
|
||||
|
||||
//TODO more compares
|
||||
constexpr bool compare(const string_base& s)const noexcept{return *this == s;}
|
||||
constexpr bool compare(basic_string_view<value_type> s)const noexcept{return *this == s;}
|
||||
constexpr bool compare(const_pointer c)const noexcept{return *this == c;}
|
||||
|
||||
constexpr size_type find_first_of(value_type v, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_first_of(const_pointer c, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_first_of(const_pointer c, size_type start, size_type size)const noexcept;
|
||||
template<class StringView>
|
||||
constexpr auto find_first_of(const StringView& str, size_type start = 0)const noexcept(
|
||||
std::is_nothrow_convertible_v<const StringView&,basic_string_view<value_type>>) ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>>,
|
||||
size_type>;
|
||||
|
||||
constexpr size_type find_last_of(value_type v, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_of(const_pointer c, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_of(const_pointer c, size_type start, size_type size)const noexcept;
|
||||
template<class StringView>
|
||||
constexpr auto find_last_of(const StringView& str, size_type start = 0)const noexcept(
|
||||
std::is_nothrow_convertible_v<const StringView&,basic_string_view<value_type>>) ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>>,
|
||||
size_type>;
|
||||
|
||||
constexpr size_type find_first_not_of(const_pointer str, size_type start, size_type count)const noexcept;
|
||||
constexpr size_type find_first_not_of(const_pointer str, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_first_not_of(value_type v, size_type start = 0)const noexcept;
|
||||
template<class StringView>
|
||||
constexpr auto find_first_not_of(const StringView& str, size_type start = 0)const noexcept(
|
||||
std::is_nothrow_convertible_v<const StringView&,basic_string_view<value_type>>) ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>>,
|
||||
size_type>;
|
||||
|
||||
constexpr size_type find_last_not_of(const_pointer str, size_type start, size_type count)const noexcept;
|
||||
constexpr size_type find_last_not_of(const_pointer str, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_not_of(value_type v, size_type start = 0)const noexcept;
|
||||
template<class StringView>
|
||||
constexpr auto find_last_not_of(const StringView& str, size_type start = 0)const noexcept(
|
||||
std::is_nothrow_convertible_v<const StringView&,basic_string_view<value_type>>) ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>>,
|
||||
size_type>;
|
||||
|
||||
constexpr reference front(void)noexcept{return *get_pointer();}
|
||||
constexpr const_reference front(void)const noexcept{return *get_pointer();}
|
||||
constexpr reference back(void)noexcept{return *(get_pointer() + length() - 1);}
|
||||
constexpr const_reference back(void)const noexcept{return *(get_pointer() + length() - 1);}
|
||||
|
||||
constexpr iterator begin(void)noexcept{return get_pointer();}
|
||||
constexpr const_iterator begin(void)const noexcept{return get_pointer();}
|
||||
constexpr iterator end(void)noexcept{return get_pointer()+length();}
|
||||
constexpr const_iterator end(void)const noexcept{return get_pointer()+length();}
|
||||
constexpr const_iterator cbegin(void)const noexcept{return begin();}
|
||||
constexpr const_iterator cend(void)const noexcept{return end();}
|
||||
|
||||
constexpr reverse_iterator rbegin(void)noexcept{return reverse_iterator(get_pointer()+length());}
|
||||
constexpr const_reverse_iterator rbegin(void)const noexcept{return const_reverse_iterator(get_pointer()+length());}
|
||||
constexpr reverse_iterator rend(void)noexcept{return reverse_iterator(get_pointer());}
|
||||
constexpr const_reverse_iterator rend(void)const noexcept{return const_reverse_iterator(get_pointer());}
|
||||
constexpr const_reverse_iterator crbegin(void)const noexcept{return rbegin();}
|
||||
constexpr const_reverse_iterator crend(void)const noexcept{return rend();}
|
||||
|
||||
constexpr void clear(void)noexcept;
|
||||
|
||||
constexpr basic_string_view<value_type> create_view(void)const noexcept;
|
||||
constexpr basic_string_view<value_type> create_view(const_iterator start, const_iterator fin)const noexcept;
|
||||
|
||||
}; //class string_base
|
||||
|
||||
|
||||
//Supplies all functions that string_base can't implement
|
||||
template<class Char, class Allocator>
|
||||
class basic_string : protected detail::hasallocator<Allocator>, public string_base<Char>
|
||||
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc = rexy::allocator<Char>>
|
||||
class basic_string : protected detail::hasallocator<Alloc>, public string_base<Char>
|
||||
{
|
||||
public:
|
||||
using value_type = typename string_base<Char>::value_type;
|
||||
@ -251,68 +400,284 @@ namespace rexy{
|
||||
using const_iterator = typename string_base<Char>::const_iterator;
|
||||
using reverse_iterator = typename string_base<Char>::reverse_iterator;
|
||||
using const_reverse_iterator = typename string_base<Char>::const_reverse_iterator;
|
||||
using allocator_type = Allocator;
|
||||
using allocator_type = Alloc;
|
||||
|
||||
static constexpr size_type npos = size_type(-1);
|
||||
|
||||
private:
|
||||
void _copy_construct_string(const_pointer data, size_type len, size_type cap)
|
||||
noexcept(noexcept(this->allocate(0)));
|
||||
basic_string& _copy_string(const_pointer s, size_type len)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void _copy_construct_string(const_pointer data, size_type len, size_type cap)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& _copy_string(const_pointer s, size_type len)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& _insert_impl(size_type pos, InputIt start, size_type insert_count)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
template<class InputIt>
|
||||
constexpr basic_string& _replace_impl(size_type pos, size_type count, InputIt src, size_type count2)noexcept;
|
||||
|
||||
public:
|
||||
constexpr basic_string(void)noexcept;
|
||||
constexpr basic_string(rexy::steal<pointer> data, size_type len)noexcept;
|
||||
constexpr basic_string(rexy::steal<pointer> data, size_type len, size_type cap)noexcept;
|
||||
constexpr basic_string(rexy::steal<pointer> data)noexcept;
|
||||
basic_string(const_pointer data, size_type len)noexcept(noexcept(this->allocate(0)));
|
||||
basic_string(const_pointer data, size_type len, size_type cap)noexcept(noexcept(this->allocate(0)));
|
||||
basic_string(const_pointer data)noexcept(noexcept(this->allocate(0)));
|
||||
explicit basic_string(size_type len)noexcept(noexcept(this->allocate(0)));
|
||||
basic_string(size_type len, size_type cap)noexcept(noexcept(this->allocate(0)));
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(const_pointer data, size_type len)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(const_pointer data, size_type len, size_type cap)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(const_pointer data)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
explicit basic_string(size_type len)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(size_type len, size_type cap)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
template<class InputIt, class Enable = std::enable_if_t<is_legacy_input_iterator_v<InputIt>,int>>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(InputIt start, InputIt fin)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(const basic_string_view<Char>& sv)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
//normal copy and move ctors
|
||||
basic_string(const basic_string& b)noexcept(noexcept(this->allocate(0)));
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(const basic_string& b)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
constexpr basic_string(basic_string&& s)noexcept;
|
||||
basic_string(const string_base<Char>&)noexcept(noexcept(this->allocate(0)));
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string(const string_base<Char>&)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
|
||||
//dtor
|
||||
~basic_string(void)noexcept(noexcept(this->deallocate(nullptr, 0)));
|
||||
REXY_CPP20_CONSTEXPR
|
||||
~basic_string(void)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
basic_string& operator=(const basic_string& s)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& operator=(const basic_string& s)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
constexpr basic_string& operator=(basic_string&& s)noexcept;
|
||||
|
||||
basic_string& operator=(const string_base<Char>& s)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& operator=(const string_base<Char>& s)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& operator=(const basic_string_view<Char>& sv)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
//Copy from c string
|
||||
basic_string& operator=(const_pointer c)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& operator=(const_pointer c)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
|
||||
//These can't be put in base because they require us to know the type of Alloc
|
||||
constexpr size_type find_first_of(const basic_string& str, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_of(const basic_string& str, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_first_not_of(const basic_string& str, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_not_of(const basic_string& str, size_type start = 0)const noexcept;
|
||||
|
||||
//Replace managed pointer. Frees existing value
|
||||
void reset(pointer val = nullptr)noexcept(noexcept(this->deallocate(nullptr,0)));
|
||||
void reset(pointer val, size_type len)noexcept(noexcept(this->deallocate(nullptr,0)));
|
||||
bool resize(size_type newsize)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void reset(pointer val = nullptr)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
void append(const_pointer data, size_type len)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
void append(const_pointer data)
|
||||
noexcept(noexcept(this->allocate(0)) &&
|
||||
noexcept(this->deallocate(nullptr,0)));
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void reset(pointer val, size_type len)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
template<class Alloc = allocator_type>
|
||||
basic_string<value_type,Alloc> substring(size_type start, size_type end)const;
|
||||
REXY_CPP20_CONSTEXPR
|
||||
bool reserve(size_type newsize)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
pointer release(void)noexcept(noexcept(this->allocate(0)));
|
||||
using detail::hasallocator<Allocator>::allocator;
|
||||
};
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void shrink_to_fit(void)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void resize(size_type newsize, value_type v = value_type())noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(size_type pos, size_type insert_count, value_type v)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(size_type pos, value_type v)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(size_type pos, const_pointer str)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(size_type pos, const_pointer str, size_type insert_count)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(size_type pos, const basic_string& other)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(size_type pos, const basic_string& other, size_type index_str, size_type count = npos)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(const_iterator pos, value_type v)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(const_iterator pos, size_type count, value_type v)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto insert(const_iterator pos, InputIt start, InputIt last)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>) ->
|
||||
std::enable_if_t<is_legacy_input_iterator_v<InputIt>,basic_string&>;
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto insert(size_type pos, InputIt start, InputIt last)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>) ->
|
||||
std::enable_if_t<is_legacy_input_iterator_v<InputIt>,basic_string&>;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& insert(const_iterator pos, std::initializer_list<value_type> list)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
template<class StringView>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto insert(size_type pos, const StringView& sv)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>) ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>> &&
|
||||
!std::is_convertible_v<const StringView&, const_pointer>,
|
||||
basic_string&>;
|
||||
|
||||
template<class StringView>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto insert(size_type pos, const StringView& sv, size_type index_str, size_type count = npos)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>) ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>> &&
|
||||
!std::is_convertible_v<const StringView&,const_pointer>,
|
||||
basic_string&>;
|
||||
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
auto append(InputIt start, InputIt fin)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>) ->
|
||||
std::enable_if_t<is_legacy_input_iterator_v<InputIt>,basic_string&>;
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& append(const_pointer data, size_type len)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& append(const_pointer data)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string& append(const basic_string& other)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
void push_back(value_type data)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
constexpr void pop_back(void)noexcept;
|
||||
|
||||
|
||||
constexpr basic_string& replace(size_type pos, size_type count, const basic_string& str)noexcept;
|
||||
constexpr basic_string& replace(const_iterator pos, const_iterator last, const basic_string& str)noexcept;
|
||||
constexpr basic_string& replace(size_type pos, size_type count, const basic_string& str, size_type pos2, size_type count2 = npos)noexcept;
|
||||
|
||||
template<class InputIt>
|
||||
constexpr auto replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2)noexcept ->
|
||||
std::enable_if_t<is_legacy_input_iterator_v<InputIt>,basic_string&>;
|
||||
|
||||
constexpr basic_string& replace(size_type pos, size_type count, const_pointer cstr, size_type count2)noexcept;
|
||||
constexpr basic_string& replace(const_iterator first, const_iterator last, const_pointer cstr, size_type count)noexcept;
|
||||
constexpr basic_string& replace(size_type pos, size_type count, const_pointer cstr)noexcept;
|
||||
constexpr basic_string& replace(const_iterator first, const_iterator last, const_pointer cstr)noexcept;
|
||||
constexpr basic_string& replace(size_type pos, size_type count, size_type count2, value_type v)noexcept;
|
||||
constexpr basic_string& replace(const_iterator first, const_iterator last, size_type count2, value_type v)noexcept;
|
||||
constexpr basic_string& replace(const_iterator first, const_iterator last, std::initializer_list<value_type> list)noexcept;
|
||||
|
||||
template<class StringView>
|
||||
constexpr auto replace(size_type pos, size_type count, const StringView& sv)noexcept ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>> &&
|
||||
!std::is_convertible_v<const StringView&,const_pointer>,
|
||||
basic_string&>;
|
||||
|
||||
template<class StringView>
|
||||
constexpr auto replace(const_iterator first, const_iterator last, const StringView& sv)noexcept ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>> &&
|
||||
!std::is_convertible_v<const StringView&,const_pointer>,
|
||||
basic_string&>;
|
||||
|
||||
template<class StringView>
|
||||
constexpr auto replace(size_type pos, size_type count, const StringView& sv, size_type pos2, size_type count2 = npos)noexcept ->
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<const StringView&, basic_string_view<value_type>> &&
|
||||
!std::is_convertible_v<const StringView&,const_pointer>,
|
||||
basic_string&>;
|
||||
|
||||
|
||||
constexpr basic_string& erase(size_type index = 0, size_type count = npos)noexcept;
|
||||
constexpr iterator erase(const_iterator pos)noexcept;
|
||||
constexpr iterator erase(const_iterator first, const_iterator last)noexcept;
|
||||
|
||||
|
||||
template<REXY_ALLOCATOR_CONCEPT A = allocator_type>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string<value_type,A> substring(size_type start, size_type end)const noexcept(
|
||||
is_nothrow_allocator_v<A>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
basic_string substr(size_type start, size_type end)const noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
REXY_CPP20_CONSTEXPR
|
||||
pointer release(void)noexcept(
|
||||
is_nothrow_allocator_v<Alloc>);
|
||||
|
||||
using detail::hasallocator<Alloc>::allocator;
|
||||
|
||||
}; //class basic_string
|
||||
|
||||
//Like an expression template but not really
|
||||
template<class Left, class Right>
|
||||
@ -340,141 +705,21 @@ namespace rexy{
|
||||
constexpr string_cat_expr(string_cat_expr&&) = default;
|
||||
|
||||
constexpr size_type length(void)const noexcept;
|
||||
template<class Alloc>
|
||||
operator basic_string<value_type,Alloc>(void)
|
||||
noexcept(std::is_nothrow_constructible<basic_string<value_type,Alloc>, typename basic_string<value_type,Alloc>::size_type>::value &&
|
||||
std::is_nothrow_invocable<detail::string_appender<basic_string<value_type,Alloc>>,decltype(*this)>::value);
|
||||
template<REXY_ALLOCATOR_CONCEPT Alloc>
|
||||
REXY_CPP20_CONSTEXPR
|
||||
operator basic_string<value_type,Alloc>(void)noexcept(
|
||||
std::is_nothrow_constructible_v<
|
||||
basic_string<value_type,Alloc>,
|
||||
typename basic_string<value_type,Alloc>::size_type> &&
|
||||
std::is_nothrow_invocable_v<
|
||||
detail::string_appender<basic_string<value_type,Alloc>>,
|
||||
decltype(*this)>);
|
||||
};
|
||||
template<class Left, class Right>
|
||||
string_cat_expr(Left&&,Right&&) -> string_cat_expr<Left&&,Right&&>;
|
||||
|
||||
template<class Char>
|
||||
class static_string : public string_base<Char>
|
||||
{
|
||||
public:
|
||||
using value_type = typename string_base<Char>::value_type;
|
||||
using size_type = typename string_base<Char>::size_type;
|
||||
using difference_type = typename string_base<Char>::difference_type;
|
||||
using pointer = typename string_base<Char>::pointer;
|
||||
using const_pointer = typename string_base<Char>::const_pointer;
|
||||
using reference = typename string_base<Char>::reference;
|
||||
using const_reference = typename string_base<Char>::const_reference;
|
||||
using iterator = typename string_base<Char>::iterator;
|
||||
using const_iterator = typename string_base<Char>::const_iterator;
|
||||
|
||||
public:
|
||||
constexpr static_string(void)noexcept;
|
||||
constexpr static_string(const_pointer str, size_type len)noexcept;
|
||||
constexpr static_string(const_pointer c)noexcept;
|
||||
constexpr static_string(const static_string& s)noexcept;
|
||||
constexpr static_string(static_string&& s)noexcept;
|
||||
~static_string(void)noexcept = default;
|
||||
|
||||
constexpr static_string& operator=(const_pointer c)noexcept;
|
||||
constexpr static_string& operator=(const static_string& s)noexcept;
|
||||
constexpr static_string& operator=(static_string&&)noexcept;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
static_string(const T*) -> static_string<T>;
|
||||
template<class T>
|
||||
static_string(const T*, size_t) -> static_string<T>;
|
||||
|
||||
|
||||
template<class T>
|
||||
struct is_string{
|
||||
static constexpr bool value = rexy::is_template_derived_type<T,string_base>::value || rexy::is_template_type<T,string_cat_expr>::value;
|
||||
};
|
||||
template<class T>
|
||||
struct is_concrete_string{
|
||||
static constexpr bool value = rexy::is_template_derived_type<T,string_base>::value;
|
||||
};
|
||||
namespace detail{
|
||||
|
||||
template<class... Ts>
|
||||
using enable_if_string = std::enable_if_t<(is_string<Ts>::value && ...),int>;
|
||||
template<class... Ts>
|
||||
using enable_if_concrete_string = std::enable_if_t<(is_concrete_string<Ts>::value && ...),int>;
|
||||
template<class... Ts>
|
||||
using enable_if_expr_string = std::enable_if_t<(rexy::is_template_type<Ts,string_cat_expr>::value && ...),int>;
|
||||
|
||||
} //namespace detail
|
||||
|
||||
template<class Str1, class Str2, detail::enable_if_concrete_string<Str1,Str2> = 0>
|
||||
constexpr bool operator==(Str1&& left, Str2&& right)noexcept{
|
||||
return left.valid() && right.valid() && left.length() == right.length() && !strcmp(left.get(), right.get());
|
||||
}
|
||||
template<class Str, detail::enable_if_concrete_string<Str> = 0>
|
||||
constexpr bool operator==(Str&& left, typename std::decay_t<Str>::const_pointer right)noexcept{
|
||||
return left.valid() && right && !strcmp(left.get(), right);
|
||||
}
|
||||
template<class Str1, class Str2, detail::enable_if_concrete_string<Str1,Str2> = 0>
|
||||
constexpr bool operator!=(Str1&& left, Str2&& right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
template<class Str, detail::enable_if_concrete_string<Str> = 0>
|
||||
constexpr bool operator!=(Str&& left, typename std::decay_t<Str>::const_pointer right)noexcept{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
template<class Left, class Right, detail::enable_if_string<Left,Right> = 0>
|
||||
constexpr auto operator+(Left&& l, Right&& r)
|
||||
//uses deduction guide whereas std::is_nothrow_constructible couldn't
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(l), std::forward<Right>(r))))
|
||||
{
|
||||
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
|
||||
}
|
||||
template<class Right, detail::enable_if_string<Right> = 0>
|
||||
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::static_string(left), std::forward<Right>(right))))
|
||||
{
|
||||
return string_cat_expr(rexy::static_string(left), std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template<class Left, detail::enable_if_string<Left> = 0>
|
||||
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
|
||||
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::static_string(right))))
|
||||
{
|
||||
return rexy::string_cat_expr(std::forward<Left>(left), rexy::static_string(right));
|
||||
}
|
||||
|
||||
template<class Left, class Right, detail::enable_if_concrete_string<Left> = 0, detail::enable_if_string<Right> = 0>
|
||||
decltype(auto) operator+=(Left& l, Right&& r)
|
||||
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
|
||||
{
|
||||
return l = (l + std::forward<Right>(r));
|
||||
}
|
||||
template<class Left, detail::enable_if_concrete_string<Left> = 0>
|
||||
decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
|
||||
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
|
||||
{
|
||||
return l = (l + r);
|
||||
}
|
||||
}
|
||||
} //namespace rexy
|
||||
|
||||
#include "string_base.tpp"
|
||||
|
||||
namespace{
|
||||
constexpr inline rexy::static_string<char> operator"" _ss(const char* str, size_t len)noexcept{
|
||||
return rexy::static_string(str, len);
|
||||
}
|
||||
constexpr inline rexy::static_string<wchar_t> operator"" _ss(const wchar_t* str, size_t len)noexcept{
|
||||
return rexy::static_string(str, len);
|
||||
}
|
||||
constexpr inline rexy::static_string<char16_t> operator"" _ss(const char16_t* str, size_t len)noexcept{
|
||||
return rexy::static_string(str, len);
|
||||
}
|
||||
constexpr inline rexy::static_string<char32_t> operator"" _ss(const char32_t* str, size_t len)noexcept{
|
||||
return rexy::static_string(str, len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef REXY_BINARY_BASE_HPP
|
||||
#include "detail/binary_string_conv.hpp"
|
||||
#endif
|
||||
#ifdef REXY_HASH_HPP
|
||||
#include "static_string_hash.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
191
include/rexy/string_view.hpp
Normal file
191
include/rexy/string_view.hpp
Normal file
@ -0,0 +1,191 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_STRING_VIEW_HPP
|
||||
#define REXY_STRING_VIEW_HPP
|
||||
|
||||
#include <cstddef> //std::size_t, ptrdiff_t
|
||||
#include <iterator> //reverse_iterator
|
||||
|
||||
#include "compat/standard.hpp"
|
||||
#include "rexy.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class Char>
|
||||
class string_base;
|
||||
|
||||
template<class Char>
|
||||
class basic_string_view
|
||||
{
|
||||
public:
|
||||
using value_type = Char;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = const_pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
static constexpr size_type npos = size_type(-1);
|
||||
|
||||
private:
|
||||
const_pointer m_data = nullptr;
|
||||
size_type m_length = 0;
|
||||
|
||||
public:
|
||||
constexpr basic_string_view(void)noexcept = default;
|
||||
constexpr basic_string_view(const_pointer str, size_type len)noexcept;
|
||||
constexpr basic_string_view(const_pointer c)noexcept;
|
||||
constexpr basic_string_view(const basic_string_view& s)noexcept = default;
|
||||
constexpr basic_string_view(const string_base<Char>& s)noexcept;
|
||||
constexpr basic_string_view(basic_string_view&& s)noexcept = default;
|
||||
template<class InIter>
|
||||
constexpr basic_string_view(InIter start, InIter fin)noexcept;
|
||||
REXY_CPP20_CONSTEXPR ~basic_string_view(void)noexcept = default;
|
||||
|
||||
constexpr basic_string_view& operator=(const_pointer c)noexcept;
|
||||
constexpr basic_string_view& operator=(const basic_string_view& s)noexcept = default;
|
||||
constexpr basic_string_view& operator=(basic_string_view&&)noexcept = default;
|
||||
|
||||
//Length of string not including null terminator
|
||||
constexpr size_type length(void)const noexcept{return m_length;}
|
||||
constexpr size_type size(void)const noexcept{return m_length;}
|
||||
//direct access to managed pointer
|
||||
constexpr const_pointer c_str(void)const noexcept{return m_data;}
|
||||
constexpr const_pointer data(void)const noexcept{return m_data;}
|
||||
//true if m_data is not empty
|
||||
constexpr bool valid(void)const noexcept{return m_length > 0;}
|
||||
constexpr bool empty(void)const noexcept{return m_length == 0;}
|
||||
|
||||
constexpr const_reference operator[](size_type i)const noexcept{return m_data[i];}
|
||||
constexpr const_reference at(size_type i)const noexcept{return m_data[i];}
|
||||
constexpr const_reference front(void)const noexcept{return m_data[0];}
|
||||
constexpr const_reference back(void)const noexcept{return m_data[m_length-1];}
|
||||
constexpr const_iterator it_at(size_type i)const noexcept{return m_data + i;}
|
||||
|
||||
constexpr const_iterator search(basic_string_view s)const noexcept;
|
||||
constexpr const_iterator search(const_pointer c)const noexcept;
|
||||
template<class Searcher>
|
||||
constexpr const_iterator search(basic_string_view s, const Searcher& searcher)const noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_iterator, const_iterator>);
|
||||
template<class Searcher>
|
||||
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>);
|
||||
|
||||
constexpr bool starts_with(basic_string_view sv)const noexcept;
|
||||
constexpr bool starts_with(value_type v)const noexcept;
|
||||
constexpr bool starts_with(const_pointer str)const noexcept;
|
||||
|
||||
constexpr bool ends_with(basic_string_view sv)const noexcept;
|
||||
constexpr bool ends_with(value_type v)const noexcept;
|
||||
constexpr bool ends_with(const_pointer str)const noexcept;
|
||||
|
||||
constexpr bool contains(basic_string_view sv)const noexcept;
|
||||
constexpr bool contains(value_type sv)const noexcept;
|
||||
constexpr bool contains(const_pointer str)const noexcept;
|
||||
|
||||
constexpr bool compare(const basic_string_view& s)const noexcept{return *this == s;}
|
||||
constexpr bool compare(const_pointer c)const noexcept{return *this == c;}
|
||||
|
||||
constexpr const_iterator begin(void)const noexcept{return m_data;}
|
||||
constexpr const_iterator end(void)const noexcept{return m_data+m_length;}
|
||||
constexpr const_iterator cbegin(void)const noexcept{return begin();}
|
||||
constexpr const_iterator cend(void)const noexcept{return end();}
|
||||
|
||||
constexpr const_reverse_iterator rbegin(void)const noexcept{return const_reverse_iterator(m_data+m_length);}
|
||||
constexpr const_reverse_iterator rend(void)const noexcept{return const_reverse_iterator(m_data);}
|
||||
constexpr const_reverse_iterator crbegin(void)const noexcept{return rbegin();}
|
||||
constexpr const_reverse_iterator crend(void)const noexcept{return rend();}
|
||||
|
||||
constexpr void remove_prefix(size_type i)noexcept;
|
||||
constexpr void remove_suffix(size_type i)noexcept;
|
||||
|
||||
constexpr basic_string_view substr(size_type pos, size_type count = npos)const noexcept;
|
||||
|
||||
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_first_of(value_type v, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_first_of(const_pointer c, size_type pos, size_type size)const noexcept;
|
||||
|
||||
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_first_not_of(value_type v, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_first_not_of(const_pointer c, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_first_not_of(const_pointer c, size_type pos, size_type size)const noexcept;
|
||||
|
||||
constexpr size_type find_last_of(basic_string_view str, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_last_of(value_type v, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_of(const_pointer c, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_last_of(const_pointer c, size_type pos, size_type size)const noexcept;
|
||||
|
||||
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_last_not_of(value_type v, size_type start = 0)const noexcept;
|
||||
constexpr size_type find_last_not_of(const_pointer c, size_type pos = 0)const noexcept;
|
||||
constexpr size_type find_last_not_of(const_pointer c, size_type pos, size_type size)const noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
basic_string_view(const T*) -> basic_string_view<T>;
|
||||
template<class T>
|
||||
basic_string_view(const T*, std::size_t) -> basic_string_view<T>;
|
||||
|
||||
using string_view = basic_string_view<char>;
|
||||
using wstring_view = basic_string_view<wchar_t>;
|
||||
|
||||
#ifndef LIBREXY_HEADER_ONLY
|
||||
extern template class basic_string_view<char>;
|
||||
extern template class basic_string_view<wchar_t>;
|
||||
extern template class basic_string_view<char16_t>;
|
||||
extern template class basic_string_view<char32_t>;
|
||||
#endif
|
||||
|
||||
inline namespace str_literals{
|
||||
|
||||
constexpr inline rexy::basic_string_view<char> operator"" _sv(const char* str, std::size_t len)noexcept{
|
||||
return rexy::basic_string_view(str, len);
|
||||
}
|
||||
constexpr inline rexy::basic_string_view<wchar_t> operator"" _sv(const wchar_t* str, std::size_t len)noexcept{
|
||||
return rexy::basic_string_view(str, len);
|
||||
}
|
||||
constexpr inline rexy::basic_string_view<char16_t> operator"" _sv(const char16_t* str, std::size_t len)noexcept{
|
||||
return rexy::basic_string_view(str, len);
|
||||
}
|
||||
constexpr inline rexy::basic_string_view<char32_t> operator"" _sv(const char32_t* str, std::size_t len)noexcept{
|
||||
return rexy::basic_string_view(str, len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace{
|
||||
template<class Char>
|
||||
std::ostream& operator<<(std::ostream& os, const rexy::basic_string_view<Char>& str){
|
||||
return os << str.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
#include "string_view.tpp"
|
||||
|
||||
|
||||
#endif
|
||||
242
include/rexy/string_view.tpp
Normal file
242
include/rexy/string_view.tpp
Normal file
@ -0,0 +1,242 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_STRING_VIEW_TPP
|
||||
#define REXY_STRING_VIEW_TPP
|
||||
|
||||
#include "compat/to_address.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "string_base.hpp"
|
||||
#include "algorithm.hpp" //two_way_search
|
||||
|
||||
#include "compat/string_base.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<class Char>
|
||||
constexpr basic_string_view<Char>::basic_string_view(const_pointer str, size_type len)noexcept:
|
||||
m_data(str), m_length(len){}
|
||||
template<class Char>
|
||||
constexpr basic_string_view<Char>::basic_string_view(const string_base<Char>& s)noexcept:
|
||||
m_data(s.c_str()), m_length(s.length()){}
|
||||
template<class Char>
|
||||
template<class InIter>
|
||||
constexpr basic_string_view<Char>::basic_string_view(InIter start, InIter fin)noexcept:
|
||||
basic_string_view(compat::to_address(start), fin - start){}
|
||||
|
||||
template<class Char>
|
||||
constexpr basic_string_view<Char>::basic_string_view(const_pointer c)noexcept:
|
||||
basic_string_view(c, rexy::strlen(c)){}
|
||||
template<class Char>
|
||||
constexpr basic_string_view<Char>& basic_string_view<Char>::operator=(const_pointer c)noexcept{
|
||||
m_data = c;
|
||||
m_length = rexy::strlen(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::search(basic_string_view s)const noexcept -> const_iterator{
|
||||
if(s.length() > m_length){
|
||||
return cend();
|
||||
}
|
||||
return two_way_search(cbegin(), cend(), s.cbegin(), s.cend());
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::search(const_pointer c)const noexcept -> const_iterator{
|
||||
basic_string_view tmp(c);
|
||||
return search(tmp);
|
||||
}
|
||||
template<class Char>
|
||||
template<class Searcher>
|
||||
constexpr auto basic_string_view<Char>::search(basic_string_view s, const Searcher& searcher)const noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_iterator, const_iterator>) -> const_iterator
|
||||
{
|
||||
if(s.length() > m_length){
|
||||
return cend();
|
||||
}
|
||||
return searcher(cbegin(), cend(), s.cbegin(), s.cend());
|
||||
}
|
||||
template<class Char>
|
||||
template<class Searcher>
|
||||
constexpr auto basic_string_view<Char>::search(const_pointer c, const Searcher& searcher)const noexcept(
|
||||
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>) -> const_iterator
|
||||
{
|
||||
basic_string_view tmp(c);
|
||||
return search(tmp, searcher);
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::starts_with(basic_string_view sv)const noexcept{
|
||||
if(sv.length() > length()){
|
||||
return false;
|
||||
}
|
||||
auto it = two_way_search(begin(), begin() + sv.length(), sv.cbegin(), sv.cend());
|
||||
if(it == begin()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::starts_with(value_type v)const noexcept{
|
||||
return front() == v;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::starts_with(const_pointer s)const noexcept{
|
||||
return starts_with(basic_string_view(s));
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::ends_with(basic_string_view sv)const noexcept{
|
||||
if(sv.length() > length()){
|
||||
return false;
|
||||
}
|
||||
const auto start = end() - sv.length();
|
||||
auto it = two_way_search(start, end(), sv.cbegin(), sv.cend());
|
||||
if(it == start){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::ends_with(value_type v)const noexcept{
|
||||
return back() == v;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::ends_with(const_pointer s)const noexcept{
|
||||
return ends_with(basic_string_view(s));
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::contains(basic_string_view sv)const noexcept{
|
||||
const auto it = two_way_search(cbegin(), cend(), sv.cbegin(), sv.cend());
|
||||
if(it != cend()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::contains(value_type v)const noexcept{
|
||||
for(size_type i = 0;i < length();++i){
|
||||
if(m_data[i] == v){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<class Char>
|
||||
constexpr bool basic_string_view<Char>::contains(const_pointer str)const noexcept{
|
||||
return contains(basic_string_view(str));
|
||||
}
|
||||
|
||||
|
||||
template<class Char>
|
||||
constexpr basic_string_view<Char> basic_string_view<Char>::substr(size_type pos, size_type count)const noexcept{
|
||||
const size_type real_count = rexy::min(count, length() - pos);
|
||||
return basic_string_view{m_data + pos, real_count};
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr void basic_string_view<Char>::remove_prefix(size_type i) noexcept{
|
||||
if(i > m_length){
|
||||
m_data = end();
|
||||
m_length = 0;
|
||||
}else{
|
||||
m_data = begin() + i;
|
||||
m_length -= i;
|
||||
}
|
||||
}
|
||||
template<class Char>
|
||||
constexpr void basic_string_view<Char>::remove_suffix(size_type i) noexcept{
|
||||
if(i > m_length){
|
||||
m_length = 0;
|
||||
}else{
|
||||
m_length -= i;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_of(basic_string_view str, size_type pos)const noexcept -> size_type{
|
||||
return rexy::find_first_of(*this, str.c_str(), pos, str.length());
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_of(value_type v, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_first_of(*this, &v, start, 1);
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_of(const_pointer c, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_first_of(*this, c, start, rexy::strlen(c));
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
|
||||
return rexy::find_first_of(*this, c, start, size);
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_not_of(basic_string_view str, size_type pos)const noexcept -> size_type{
|
||||
return rexy::find_first_not_of(*this, str.c_str(), pos, str.length());
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_not_of(value_type v, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_first_not_of(*this, &v, start, 1);
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_not_of(const_pointer c, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_first_not_of(*this, c, start, rexy::strlen(c));
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_first_not_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
|
||||
return rexy::find_first_not_of(*this, c, start, size);
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_of(basic_string_view str, size_type pos)const noexcept -> size_type{
|
||||
return rexy::find_last_of(*this, str.c_str(), pos, str.length());
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_of(value_type v, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_last_of(*this, &v, start, 1);
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_of(const_pointer c, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_last_of(*this, c, start, rexy::strlen(c));
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
|
||||
return rexy::find_last_of(*this, c, start, size);
|
||||
}
|
||||
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_not_of(basic_string_view str, size_type pos)const noexcept -> size_type{
|
||||
return rexy::find_last_not_of(*this, str.c_str(), pos, str.length());
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_not_of(value_type v, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_last_not_of(*this, &v, start, 1);
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_not_of(const_pointer c, size_type start)const noexcept -> size_type{
|
||||
return rexy::find_last_not_of(*this, c, start, rexy::strlen(c));
|
||||
}
|
||||
template<class Char>
|
||||
constexpr auto basic_string_view<Char>::find_last_not_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
|
||||
return rexy::find_last_not_of(*this, c, start, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -29,6 +29,7 @@
|
||||
#include <future> //future, packaged_task
|
||||
#include <functional> //function, bind
|
||||
#include <utility> //move, forward
|
||||
#include <type_traits> //invoke_result_t
|
||||
|
||||
#include "rexy.hpp"
|
||||
|
||||
@ -62,14 +63,14 @@ namespace rexy{
|
||||
void invalidate(void);
|
||||
|
||||
template<class Func, class... Args>
|
||||
auto add_job(Func&& f, Args&&... args) -> std::future<decltype(std::forward<Func>(f)(std::forward<Args>(args)...))>;
|
||||
auto add_job(Func&& f, Args&&... args) -> std::future<std::invoke_result_t<Func,Args...>>;
|
||||
|
||||
private:
|
||||
void worker_loop(void);
|
||||
};
|
||||
|
||||
template<class Func, class... Args>
|
||||
auto threadpool::add_job(Func&& f, Args&&... args) -> std::future<decltype(std::forward<Func>(f)(std::forward<Args>(args)...))>{
|
||||
auto threadpool::add_job(Func&& f, Args&&... args) -> std::future<std::invoke_result_t<Func,Args...>>{
|
||||
using return_t = decltype(std::forward<Func>(f)(std::forward<Args>(args)...));
|
||||
using task_t = std::packaged_task<return_t(void)>;
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -20,7 +20,9 @@
|
||||
#define REXY_TRAITS_HPP
|
||||
|
||||
#include <type_traits> //is_same, decay, integral_constant, declval
|
||||
#include <iterator> //iterator_traits
|
||||
|
||||
#include "compat/traits.hpp"
|
||||
#include "rexy.hpp"
|
||||
|
||||
namespace rexy{
|
||||
@ -30,7 +32,7 @@ namespace rexy{
|
||||
static std::true_type check(U*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = std::is_same<std::true_type,decltype(check(std::declval<std::decay_t<T>*>()))>::value;
|
||||
static constexpr bool value = decltype(check(std::declval<std::decay_t<T>*>()))::value;
|
||||
};
|
||||
|
||||
template<class T, template<class...> class U>
|
||||
@ -54,6 +56,86 @@ namespace rexy{
|
||||
};
|
||||
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_dereferencable : public std::false_type{};
|
||||
template<class T>
|
||||
struct is_dereferencable<T,std::void_t<decltype(*(std::declval<T>()))>> : public std::true_type{};
|
||||
template<class T>
|
||||
static constexpr bool is_dereferencable_v = is_dereferencable<T>::value;
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_pointer_dereferencable : public std::false_type{};
|
||||
template<class T>
|
||||
struct is_pointer_dereferencable<T,std::void_t<decltype(std::declval<T>().operator->())>> : public std::true_type{};
|
||||
template<class T>
|
||||
struct is_pointer_dereferencable<T*,std::void_t<T*>> : public std::true_type{};
|
||||
template<class T>
|
||||
static constexpr bool is_pointer_dereferencable_v = is_pointer_dereferencable<T>::value;
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_prefix_incrementable : public std::false_type{};
|
||||
template<class T>
|
||||
struct is_prefix_incrementable<T,std::void_t<decltype(++(std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()))>> : public std::true_type{};
|
||||
template<class T>
|
||||
static constexpr bool is_prefix_incrementable_v = is_prefix_incrementable<T>::value;
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_postfix_incrementable : public std::false_type{};
|
||||
template<class T>
|
||||
struct is_postfix_incrementable<T,std::void_t<decltype((std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>())++)>> : public std::true_type{};
|
||||
template<class T>
|
||||
static constexpr bool is_postfix_incrementable_v = is_postfix_incrementable<T>::value;
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_equality_comparable : public std::false_type{};
|
||||
template<class T>
|
||||
struct is_equality_comparable<T,std::void_t<decltype(std::declval<T>() == std::declval<T>())>> : public std::true_type{};
|
||||
template<class T>
|
||||
static constexpr bool is_equality_comparable_v = is_equality_comparable<T>::value;
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_inequality_comparable : public std::false_type{};
|
||||
template<class T>
|
||||
struct is_inequality_comparable<T,std::void_t<decltype(std::declval<T>() != std::declval<T>())>> : public std::true_type{};
|
||||
template<class T>
|
||||
static constexpr bool is_inequality_comparable_v = is_inequality_comparable<T>::value;
|
||||
|
||||
template<class T>
|
||||
struct is_legacy_iterator{
|
||||
static constexpr bool value = is_prefix_incrementable_v<T> && is_dereferencable_v<T>;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_legacy_iterator_v = is_legacy_iterator<T>::value;
|
||||
|
||||
template<class T, bool = is_legacy_iterator_v<T> &&
|
||||
is_inequality_comparable_v<T> &&
|
||||
is_pointer_dereferencable_v<T> &&
|
||||
is_postfix_incrementable_v<T>>
|
||||
struct is_legacy_input_iterator : public std::false_type{};
|
||||
|
||||
template<class T>
|
||||
struct is_legacy_input_iterator<T,true>{
|
||||
static constexpr bool value = std::is_convertible_v<decltype(std::declval<T>() == std::declval<T>()),bool> &&
|
||||
std::is_same_v<decltype(*(std::declval<T>())),typename std::iterator_traits<T>::reference> &&
|
||||
std::is_same_v<decltype(++std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()),std::add_lvalue_reference_t<T>> &&
|
||||
std::is_convertible_v<decltype(*std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()++), typename std::iterator_traits<T>::value_type>;
|
||||
};
|
||||
template<class T>
|
||||
static constexpr bool is_legacy_input_iterator_v = is_legacy_input_iterator<T>::value;
|
||||
|
||||
template<class T, class Value, bool = is_legacy_iterator_v<T> &&
|
||||
is_postfix_incrementable_v<T>>
|
||||
struct is_legacy_output_iterator : public std::false_type{};
|
||||
|
||||
template<class T, class Value>
|
||||
struct is_legacy_output_iterator<T,Value,true>{
|
||||
static constexpr bool value =
|
||||
std::is_same_v<decltype(++std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()),std::add_lvalue_reference_t<T>> &&
|
||||
std::is_convertible_v<decltype(std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()++), std::add_lvalue_reference_t<std::add_const_t<T>>>;
|
||||
};
|
||||
template<class T, class Value>
|
||||
static constexpr bool is_legacy_output_iterator_v = is_legacy_output_iterator<T,Value>::value;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,10 +20,18 @@
|
||||
#define REXY_UTILITY_HPP
|
||||
|
||||
#include <utility> //forward, move
|
||||
#include <cstdlib> //size_t
|
||||
#include <type_traits>
|
||||
#include <cstddef> //size_t
|
||||
#include <type_traits> //too many to enumerate
|
||||
#include <string> //char_traits
|
||||
|
||||
#include "compat/if_consteval.hpp"
|
||||
#include "rexy.hpp"
|
||||
#include "storage_for.hpp"
|
||||
|
||||
#ifdef REXY_if_consteval
|
||||
#include <cstring> //strlen, strcmp, memcpy
|
||||
#include <cwchar> //wcslen
|
||||
#endif
|
||||
|
||||
namespace rexy{
|
||||
|
||||
@ -46,7 +54,7 @@ namespace rexy{
|
||||
}
|
||||
return start2;
|
||||
}
|
||||
template<class T, size_t N>
|
||||
template<class T, std::size_t N>
|
||||
constexpr void swap(T (&l)[N], T (&r)[N])
|
||||
noexcept(noexcept(swap(*l, *r)))
|
||||
{
|
||||
@ -84,11 +92,70 @@ namespace rexy{
|
||||
return cmp(l, r) ? l : r;
|
||||
}
|
||||
template<class T>
|
||||
constexpr size_t strlen(const T* c)noexcept{
|
||||
size_t i = 0;
|
||||
for(;c[i];++i);
|
||||
return i;
|
||||
constexpr T abs(const T& val){
|
||||
return val > 0 ? val : -val;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr std::size_t strlen(const T* c)noexcept{
|
||||
return std::char_traits<T>::length(c);
|
||||
}
|
||||
|
||||
#ifdef REXY_if_consteval
|
||||
template<class T>
|
||||
constexpr int strcmp(const T* l, const T* r)noexcept{
|
||||
REXY_if_not_consteval{
|
||||
if constexpr(std::is_same_v<std::remove_cvref_t<T>,char>){
|
||||
return std::strcmp(l, r);
|
||||
}else if constexpr(std::is_same_v<std::remove_cvref_t<T>,wchar_t>){
|
||||
return std::wcscmp(l, r);
|
||||
}
|
||||
}
|
||||
for(;*l == *r && *l;++l, ++r);
|
||||
return *l - *r;
|
||||
}
|
||||
template<class T>
|
||||
constexpr int strncmp(const T* l, const T* r, std::size_t max)noexcept{
|
||||
REXY_if_not_consteval{
|
||||
if constexpr(std::is_same_v<std::remove_cvref_t<T>,char>){
|
||||
return std::strncmp(l, r, max);
|
||||
}else if constexpr (std::is_same_v<std::remove_cvref_t<T>,wchar_t>){
|
||||
return std::wcsncmp(l, r, max);
|
||||
}
|
||||
}
|
||||
for(std::size_t i = 1;*l == *r && *l && i < max;++i, ++l, ++r);
|
||||
return *l - *r;
|
||||
}
|
||||
template<class T, class Compare>
|
||||
constexpr int strncmp(const T* l, const T* r, std::size_t max, Compare cmp)noexcept{
|
||||
REXY_if_not_consteval{
|
||||
if constexpr(std::is_same_v<std::remove_cvref_t<T>,char>){
|
||||
return std::strncmp(l, r, max);
|
||||
}else if constexpr (std::is_same_v<std::remove_cvref_t<T>,wchar_t>){
|
||||
return std::wcsncmp(l, r, max);
|
||||
}
|
||||
}
|
||||
for(std::size_t i = 1;cmp(l, r) && *l && i < max;++i, ++l, ++r);
|
||||
return *l - *r;
|
||||
}
|
||||
template<class T, class Compare>
|
||||
constexpr int strcmp(const T* l, const T* r, Compare cmp)noexcept{
|
||||
for(;cmp(*l, *r) && *l;++l, ++r);
|
||||
return *l - *r;
|
||||
}
|
||||
constexpr void memcpy(void* l, const void* r, std::size_t n){
|
||||
REXY_if_not_consteval{
|
||||
std::memcpy(l, r, n);
|
||||
}else{
|
||||
char* ld = static_cast<char*>(l);
|
||||
const char* rd = static_cast<const char*>(r);
|
||||
for(std::size_t i = 0;i < n;++i){
|
||||
ld[i] = rd[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // REXY_if_consteval
|
||||
template<class T>
|
||||
constexpr int strcmp(const T* l, const T* r)noexcept{
|
||||
for(;*l == *r && *l;++l, ++r);
|
||||
@ -99,14 +166,66 @@ namespace rexy{
|
||||
for(;cmp(*l, *r) && *l;++l, ++r);
|
||||
return *l - *r;
|
||||
}
|
||||
constexpr void memcpy(void* l, const void* r, size_t n){
|
||||
template<class T>
|
||||
constexpr int strncmp(const T* l, const T* r, std::size_t max)noexcept{
|
||||
for(std::size_t i = 0;*l == *r && *l && i < max;++i, ++l, ++r);
|
||||
return *l - *r;
|
||||
}
|
||||
constexpr void memcpy(void* l, const void* r, std::size_t n){
|
||||
char* ld = static_cast<char*>(l);
|
||||
const char* rd = static_cast<const char*>(r);
|
||||
for(size_t i = 0;i < n;++i){
|
||||
for(std::size_t i = 0;i < n;++i){
|
||||
ld[i] = rd[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // REXY_if_consteval
|
||||
|
||||
|
||||
template<class T>
|
||||
struct constant_iterator
|
||||
{
|
||||
rexy::storage_for<T> val;
|
||||
|
||||
constexpr constant_iterator& operator++(void)noexcept{return *this;}
|
||||
constexpr constant_iterator operator++(int)noexcept{return *this;}
|
||||
constexpr T operator*(void)const noexcept{return val;}
|
||||
|
||||
constexpr bool operator==(const constant_iterator& other)const{return val == other.val;}
|
||||
constexpr bool operator!=(const constant_iterator& other)const{return val != other.val;}
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
constant_iterator(T&&) -> constant_iterator<std::remove_cvref_t<T>>;
|
||||
|
||||
template<class T>
|
||||
struct sized_constant_iterator
|
||||
{
|
||||
rexy::storage_for<T> val;
|
||||
std::size_t count;
|
||||
|
||||
constexpr sized_constant_iterator& operator++(void)noexcept{
|
||||
--count;
|
||||
return *this;
|
||||
}
|
||||
constexpr sized_constant_iterator operator++(int)noexcept{
|
||||
sized_constant_iterator other(*this);
|
||||
--count;
|
||||
return other;
|
||||
}
|
||||
constexpr const T& operator*(void)const noexcept{return val;}
|
||||
|
||||
constexpr bool operator==(const sized_constant_iterator& other)const{
|
||||
return count == other.count;
|
||||
}
|
||||
constexpr bool operator!=(const sized_constant_iterator& other)const{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
template<class T>
|
||||
sized_constant_iterator(T&&, std::size_t) -> sized_constant_iterator<std::remove_cvref_t<T>>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,18 +16,17 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_BASIC_STRING_HASH_HPP
|
||||
#define REXY_BASIC_STRING_HASH_HPP
|
||||
|
||||
#include "string_hash.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#include "rexy.hpp"
|
||||
#ifndef REXY_VISITOR_HPP
|
||||
#define REXY_VISITOR_HPP
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template<>
|
||||
struct hash<rexy::string> : public string_hash<rexy::string>{};
|
||||
template<class... Funs>
|
||||
struct visitor : public Funs...{
|
||||
using Funs::operator()...;
|
||||
};
|
||||
template<class... Funs>
|
||||
visitor(Funs...) -> visitor<Funs...>;
|
||||
|
||||
}
|
||||
|
||||
48
src/demangle.cpp
Normal file
48
src/demangle.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rexy/demangle.hpp"
|
||||
|
||||
#if defined(__GNUC__) && !defined(_MSC_VER)
|
||||
|
||||
#include <cstdlib> //free
|
||||
#include <string>
|
||||
#include <memory> //unique_ptr
|
||||
#include <cxxabi.h> //__cxa_demangle
|
||||
|
||||
namespace util{
|
||||
std::string demangle(const char* name){
|
||||
int status = 0;
|
||||
std::unique_ptr<char,void(*)(void*)> res{
|
||||
abi::__cxa_demangle(name, nullptr, nullptr, &status),
|
||||
std::free
|
||||
};
|
||||
return (status == 0) ? res.get() : name;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <string>
|
||||
namespace util{
|
||||
std::string demangle(const char* name){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,24 +1,36 @@
|
||||
//Never actually used in the project. This just ensures that all syntax is correct during builds.
|
||||
|
||||
#include "rexy/rexy.hpp"
|
||||
#include "rexy/algorithm.hpp"
|
||||
#include "rexy/allocator.hpp"
|
||||
#include "rexy/binary.hpp"
|
||||
#include "rexy/binary_base.hpp"
|
||||
#include "rexy/binary_base.tpp"
|
||||
#include "rexy/buffer.hpp"
|
||||
#include "rexy/buffer.tpp"
|
||||
#include "rexy/expression.hpp"
|
||||
#include "rexy/filerd.hpp"
|
||||
#include "rexy/hash.hpp"
|
||||
#include "rexy/mpmc_queue.hpp"
|
||||
#include "rexy/steal.hpp"
|
||||
#include "rexy/string_base.hpp"
|
||||
#include "rexy/string_base.tpp"
|
||||
#include "rexy/string_hash.hpp"
|
||||
#include "rexy/string.hpp"
|
||||
#include "rexy/traits.hpp"
|
||||
#include "rexy/utility.hpp"
|
||||
#include "rexy/meta.hpp"
|
||||
#include "rexy/enum_traits.hpp"
|
||||
#include "rexy/deferred.hpp"
|
||||
#include "rexy/debug_print.hpp"
|
||||
#include "rexy/storage_for.hpp"
|
||||
#include "rexy/visitor.hpp"
|
||||
#include "rexy/string_view.hpp"
|
||||
#include "rexy/string_view.tpp"
|
||||
#include "rexy/list.hpp"
|
||||
#include "rexy/list.tpp"
|
||||
|
||||
#ifndef LIBREXY_HEADER_ONLY
|
||||
#include "rexy/filerd.hpp"
|
||||
#include "rexy/threadpool.hpp"
|
||||
#include "rexy/demangle.hpp"
|
||||
#endif
|
||||
|
||||
#include "rexy/detail/binary_string_conv.hpp"
|
||||
#include "rexy/detail/string_appender.hpp"
|
||||
|
||||
#include "rexy/cx/array.hpp"
|
||||
@ -27,3 +39,6 @@
|
||||
#include "rexy/cx/vector.hpp"
|
||||
|
||||
#include "rexy/cx/detail/bool_specialize_base.hpp"
|
||||
|
||||
#include "rexy/compat/to_address.hpp"
|
||||
#include "rexy/compat/source_location.hpp"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
This file is a part of rexy's general purpose library
|
||||
Copyright (C) 2020 rexy712
|
||||
Copyright (C) 2020-2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,6 +16,12 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
//Disable warning from msvc for not using fopen_s
|
||||
//which is not standard in c++ as of c++23, though it is in c since c11.
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "rexy/filerd.hpp"
|
||||
|
||||
#include <cstdio> //fopen, fclose
|
||||
@ -25,89 +31,99 @@
|
||||
namespace rexy{
|
||||
|
||||
filerd::filerd(const char* f, const char* mode)noexcept:
|
||||
m_fp(fopen(f, mode)){}
|
||||
m_fp(std::fopen(f, mode)){}
|
||||
filerd::~filerd(void)noexcept{
|
||||
if(m_fp)
|
||||
fclose(m_fp);
|
||||
std::fclose(m_fp);
|
||||
}
|
||||
|
||||
void filerd::reset(FILE* fp)noexcept{
|
||||
void filerd::reset(std::FILE* fp)noexcept{
|
||||
if(m_fp)
|
||||
fclose(m_fp);
|
||||
std::fclose(m_fp);
|
||||
m_fp = fp;
|
||||
}
|
||||
FILE* filerd::release(void)noexcept{
|
||||
std::FILE* filerd::release(void)noexcept{
|
||||
return std::exchange(m_fp, nullptr);
|
||||
}
|
||||
size_t filerd::length(void)noexcept{
|
||||
std::size_t filerd::length(void)noexcept{
|
||||
if(!m_fp)
|
||||
return 0;
|
||||
size_t tmp, ret;
|
||||
tmp = ftell(m_fp);
|
||||
fseek(m_fp, 0, SEEK_END);
|
||||
ret = ftell(m_fp);
|
||||
fseek(m_fp, tmp, SEEK_SET);
|
||||
std::size_t tmp, ret;
|
||||
tmp = std::ftell(m_fp);
|
||||
std::fseek(m_fp, 0, SEEK_END);
|
||||
ret = std::ftell(m_fp);
|
||||
std::fseek(m_fp, long(tmp), SEEK_SET);
|
||||
return ret;
|
||||
}
|
||||
size_t filerd::position(void)const noexcept{
|
||||
return ftell(m_fp);
|
||||
std::size_t filerd::position(void)const noexcept{
|
||||
return std::ftell(m_fp);
|
||||
}
|
||||
void filerd::rewind(size_t pos)noexcept{
|
||||
fseek(m_fp, pos, SEEK_SET);
|
||||
void filerd::rewind(std::size_t pos)noexcept{
|
||||
std::fseek(m_fp, long(pos), SEEK_SET);
|
||||
}
|
||||
|
||||
filerd::operator FILE*(void)noexcept{
|
||||
filerd::operator std::FILE*(void)noexcept{
|
||||
return m_fp;
|
||||
}
|
||||
filerd::operator const FILE*(void)const noexcept{
|
||||
filerd::operator const std::FILE*(void)const noexcept{
|
||||
return m_fp;
|
||||
}
|
||||
FILE* filerd::get(void)noexcept{
|
||||
std::FILE* filerd::get(void)noexcept{
|
||||
return m_fp;
|
||||
}
|
||||
const FILE* filerd::get(void)const noexcept{
|
||||
const std::FILE* filerd::get(void)const noexcept{
|
||||
return m_fp;
|
||||
}
|
||||
filerd::operator bool(void)const noexcept{
|
||||
return m_fp;
|
||||
}
|
||||
|
||||
size_t filerd::read(char* dest, size_t bytes)noexcept{
|
||||
return fread(dest, 1, bytes, m_fp);
|
||||
bool filerd::eof(void)const{
|
||||
return m_finished;
|
||||
}
|
||||
rexy::string filerd::read(size_t bytes)noexcept{
|
||||
|
||||
std::size_t filerd::read(char* dest, std::size_t bytes)noexcept{
|
||||
const auto rdcnt = std::fread(dest, 1, bytes, m_fp);
|
||||
if(rdcnt < bytes){
|
||||
m_finished = true;
|
||||
}
|
||||
return rdcnt;
|
||||
}
|
||||
rexy::string filerd::read(std::size_t bytes)noexcept{
|
||||
rexy::string ret;
|
||||
char* tmp = ret.allocator().allocate(bytes);
|
||||
size_t written = read(tmp, bytes);
|
||||
std::size_t written = read(tmp, bytes);
|
||||
ret.reset(tmp, written);
|
||||
return ret;
|
||||
}
|
||||
rexy::string filerd::readln(size_t max)noexcept{
|
||||
rexy::string filerd::readln(std::size_t max)noexcept{
|
||||
rexy::string ret;
|
||||
int c;
|
||||
size_t count = 0;
|
||||
for(c = fgetc(m_fp);c != EOF && c != '\n';c = fgetc(m_fp)){
|
||||
std::size_t count = 0;
|
||||
for(c = std::fgetc(m_fp);c != EOF && c != '\n';c = std::fgetc(m_fp)){
|
||||
char ch = c;
|
||||
ret.append(&ch, 1);
|
||||
if(++count == max)
|
||||
break;
|
||||
}
|
||||
if(c == EOF){
|
||||
m_finished = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
rexy::binary filerd::read_bin(size_t bytes)
|
||||
noexcept(std::is_nothrow_constructible<rexy::binary, rexy::steal<char*>, size_t, size_t>::value)
|
||||
rexy::buffer<char> filerd::read_bin(std::size_t bytes)
|
||||
noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, std::size_t>::value)
|
||||
{
|
||||
rexy::binary ret;
|
||||
char* tmp = ret.allocator().allocate(bytes);
|
||||
size_t written = read(tmp, bytes);
|
||||
ret.reset(tmp, written);
|
||||
rexy::buffer<char> ret{bytes};
|
||||
const auto written = read(ret.data(), bytes);
|
||||
ret.set_size(written);
|
||||
return ret;
|
||||
}
|
||||
size_t filerd::write(const char* c, size_t bytes)noexcept{
|
||||
return fwrite(c, 1, bytes, m_fp);
|
||||
std::size_t filerd::write(const char* c, std::size_t bytes)noexcept{
|
||||
return std::fwrite(c, 1, bytes, m_fp);
|
||||
}
|
||||
size_t filerd::write(const rexy::string_base<char>& c)noexcept{
|
||||
return write(c.get(), c.length());
|
||||
std::size_t filerd::write(rexy::string_view c)noexcept{
|
||||
return write(c.data(), c.length());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -25,5 +25,8 @@ namespace rexy{
|
||||
template class basic_string<wchar_t,allocator<wchar_t>>;
|
||||
template class basic_string<char16_t,allocator<char16_t>>;
|
||||
template class basic_string<char32_t,allocator<char32_t>>;
|
||||
#ifdef __cpp_char8_t
|
||||
template class basic_string<char8_t,allocator<char8_t>>;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@ -16,13 +16,13 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rexy/string_base.hpp"
|
||||
#include "rexy/string_view.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
template class static_string<char>;
|
||||
template class static_string<wchar_t>;
|
||||
template class static_string<char16_t>;
|
||||
template class static_string<char32_t>;
|
||||
template class basic_string_view<char>;
|
||||
template class basic_string_view<wchar_t>;
|
||||
template class basic_string_view<char16_t>;
|
||||
template class basic_string_view<char32_t>;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user