From bcc444d3017ee967090eb19319add5976463ee5a Mon Sep 17 00:00:00 2001 From: Rick de Water Date: Fri, 24 Jan 2020 22:49:45 +0100 Subject: [PATCH] Add copy function to string and string_view #40 #41 --- src/lib/lingo/encoding/point_iterator.hpp | 23 +++++++++++---- src/lib/lingo/string.hpp | 36 ++++++++++++++++++----- src/lib/lingo/string_storage.hpp | 3 +- src/lib/lingo/string_view.hpp | 16 +++++++++- src/lib/lingo/utility/type_traits.hpp | 10 ++++++- tests/lingo/string.cpp | 27 +++++++++++++++++ tests/lingo/string_view.cpp | 27 +++++++++++++++++ 7 files changed, 125 insertions(+), 17 deletions(-) diff --git a/src/lib/lingo/encoding/point_iterator.hpp b/src/lib/lingo/encoding/point_iterator.hpp index 6fad7df..7b4069c 100644 --- a/src/lib/lingo/encoding/point_iterator.hpp +++ b/src/lib/lingo/encoding/point_iterator.hpp @@ -38,26 +38,34 @@ namespace lingo LINGO_CONSTEXPR14 point_iterator() noexcept: _current(nullptr), _end(nullptr), + _last(nullptr), _code_point(0) { } template - point_iterator(const basic_string& string): - _current(string.data()), - _end(string.data() + string.size()), + point_iterator(const basic_string& str): + _current(str.data()), + _end(str.data() + str.size()), + _last(str.data()), _code_point(parse()) { } template - LINGO_CONSTEXPR14 point_iterator(basic_string_view string): - _current(string.data()), - _end(string.data() + string.size()), + LINGO_CONSTEXPR14 point_iterator(basic_string_view str): + _current(str.data()), + _end(str.data() + str.size()), + _last(str.data()), _code_point(parse()) { } + LINGO_CONSTEXPR14 const unit_type* read_ptr() const noexcept + { + return _last; + } + LINGO_CONSTEXPR14 const_reference operator * () const noexcept { return _code_point; @@ -99,6 +107,7 @@ namespace lingo { _current = nullptr; _end = nullptr; + _last = nullptr; return {}; } @@ -111,6 +120,7 @@ namespace lingo } // Move the current pointer + _last = _current; _current += result.size; assert(_current <= _end); // _current should never go beyond _end @@ -120,6 +130,7 @@ namespace lingo const unit_type* _current; const unit_type* _end; + const unit_type* _last; point_type _code_point; }; diff --git a/src/lib/lingo/string.hpp b/src/lib/lingo/string.hpp index a27c355..a492991 100644 --- a/src/lib/lingo/string.hpp +++ b/src/lib/lingo/string.hpp @@ -21,6 +21,7 @@ #include +#include #include #include @@ -70,8 +71,6 @@ namespace lingo using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - using object_builder = utility::object_builder; - static LINGO_CONSTEXPR11 size_type npos = static_cast(-1); static LINGO_CONSTEXPR11 unit_type null_terminator = unit_type{}; static LINGO_CONSTEXPR11 bool is_execution_set = lingo::utility::is_execution_set::value; @@ -80,6 +79,7 @@ namespace lingo static_assert(std::is_same::value, "page_type::point_type must be the same type as encoding_type::point_type"); static_assert(std::is_same::value, "allocator_type::value_type must be the same type as encoding_type::unit_type"); + using object_builder = utility::object_builder; using storage_type = basic_string_storage; using string_view = basic_string_view; @@ -106,12 +106,24 @@ namespace lingo { } + template ::type = 0> + basic_string(const_pointer cstring, size_type count, const allocator_type& allocator = allocator_type()): + basic_string(string_view(cstring, count), allocator) + { + } + template ::type = 0> explicit basic_string(const_pointer cstring, const allocator_type& allocator = allocator_type()): basic_string(string_view(cstring), allocator) { } + template ::type = 0> + explicit basic_string(const_pointer cstring, size_type count, const allocator_type& allocator = allocator_type()): + basic_string(string_view(cstring, count), allocator) + { + } + basic_string(string_view string_view, const allocator_type& allocator = allocator_type()): basic_string(allocator) { @@ -657,11 +669,16 @@ namespace lingo _storage.set_size(new_size); } - basic_string substr(size_type pos = 0, size_type count = npos) + basic_string substr(size_type pos = 0, size_type count = npos) const { return basic_string(*this, pos, count); } + size_type copy(value_type* dest, size_type count, size_type pos = 0) const + { + return view().copy(dest, count, pos); + } + template basic_string& operator += (const basic_string& other) { @@ -715,7 +732,7 @@ namespace lingo operator string_view() const noexcept { - return string_view(data(), size(), true); + return view(); } const_pointer c_str() const noexcept @@ -729,15 +746,20 @@ namespace lingo return std::basic_string(data(), size()); } + string_view view() const noexcept + { + return string_view(data(), size(), true); + } + template - LINGO_CONSTEXPR14 int compare(const basic_string& other) const noexcept(noexcept(std::declval().compare(other.operator lingo::basic_string_view()))) + LINGO_CONSTEXPR14 int compare(const basic_string& other) const noexcept(noexcept(std::declval().compare(other.view()))) { - return compare(other.operator lingo::basic_string_view()); + return compare(other.view()); } LINGO_CONSTEXPR14 int compare(basic_string_view other) const { - return operator lingo::basic_string_view().compare(other); + return view().compare(other); } template ; - static_assert(std::is_same::value, "allocator_type::value_type must be the same type as basic_string_storage::unit_type"); private: using compressed_pair = utility::compressed_pair, allocator_type>; + using object_builder = utility::object_builder; struct allocation { diff --git a/src/lib/lingo/string_view.hpp b/src/lib/lingo/string_view.hpp index 6638359..fd51ca7 100644 --- a/src/lib/lingo/string_view.hpp +++ b/src/lib/lingo/string_view.hpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -62,6 +63,7 @@ namespace lingo private: static_assert(std::is_same::value, "page_type::point_type must be the same type as encoding_type::point_type"); + using object_builder = utility::object_builder; using point_iterator = encoding::point_iterator; using storage_type = basic_string_view_storage; @@ -211,8 +213,13 @@ namespace lingo } #endif - basic_string_view substr(size_type pos = 0, size_type count = npos) + basic_string_view substr(size_type pos = 0, size_type count = npos) const { + if (pos > size()) + { + throw std::out_of_range("pos > size()"); + } + const const_pointer d = data() + pos; const size_type s = std::min(count, size() - pos); const bool nt = null_terminated() && (s == size() - pos); @@ -250,6 +257,13 @@ namespace lingo } } + LINGO_CONSTEXPR14 size_type copy(value_type* dest, size_type count, size_type pos = 0) const + { + const basic_string_view str = substr(pos, count); + object_builder::copy_construct(dest, str.data(), str.size()); + return str.size(); + } + LINGO_CONSTEXPR14 void swap(basic_string_view& other) noexcept { _storage.swap(other._storage); diff --git a/src/lib/lingo/utility/type_traits.hpp b/src/lib/lingo/utility/type_traits.hpp index e3d4e4d..567c188 100644 --- a/src/lib/lingo/utility/type_traits.hpp +++ b/src/lib/lingo/utility/type_traits.hpp @@ -59,7 +59,7 @@ namespace lingo template struct is_catagory_iterator::iterator_catagory, Catagory>::value>::type> : std::true_type + typename std::enable_if::iterator_catagory>::value>::type> : std::true_type { }; @@ -78,6 +78,10 @@ namespace lingo using is_bidirectional_iterator = is_catagory_iterator; template using is_random_access_iterator = is_catagory_iterator; + #ifdef __cpp_lib_ranges + template + using is_contiguous_iterator = is_catagory_iterator; + #endif #ifdef __cpp_variable_templates template @@ -90,6 +94,10 @@ namespace lingo LINGO_CONSTEXPR14 bool is_bidirectional_iterator_v = is_bidirectional_iterator::value; template LINGO_CONSTEXPR14 bool is_random_access_iterator_v = is_random_access_iterator::value; + #ifdef __cpp_lib_ranges + template + LINGO_CONSTEXPR14 bool is_contiguous_iterator_v = is_contiguous_iterator::value; + #endif #endif } } diff --git a/tests/lingo/string.cpp b/tests/lingo/string.cpp index 8c7efcd..45caa09 100644 --- a/tests/lingo/string.cpp +++ b/tests/lingo/string.cpp @@ -671,4 +671,31 @@ LINGO_UNIT_TEST_CASE("A cstring can be concatenated to a string_view") REQUIRE(string_view_type(suffix_test_string_result.data(), source_string.size()) == source_string); REQUIRE(string_view_type(suffix_test_string_result.data() + source_string.size(), source_string.size()) == source_string); +} + +LINGO_UNIT_TEST_CASE("string can be copied to an array") +{ + LINGO_UNIT_TEST_TYPEDEFS; + + const unit_type* data = lingo::test::test_string::value; + const size_type size = sizeof(lingo::test::test_string::value) / sizeof(lingo::test::test_string::value[0]) - 1; + const string_type source(data, size); + + for (auto pos_it = point_iterator_type(source); pos_it != point_iterator_type(); ++pos_it) + { + const size_type pos = pos_it.read_ptr() - source.data(); + + for (auto size_it = pos_it; size_it != point_iterator_type(); ++size_it) + { + const size_type buffer_size = (size_it.read_ptr() - source.data()) - pos; + auto buffer = std::make_unique(buffer_size); + + const size_type copied_count = source.copy(buffer.get(), buffer_size, pos); + + REQUIRE(copied_count == buffer_size); + REQUIRE(string_view_type(buffer.get(), buffer_size, false) == source.substr(pos, buffer_size)); + } + } + + REQUIRE_THROWS_AS(source.copy(nullptr, 0, size + 1), std::out_of_range); } \ No newline at end of file diff --git a/tests/lingo/string_view.cpp b/tests/lingo/string_view.cpp index 6c42053..fdf06c4 100644 --- a/tests/lingo/string_view.cpp +++ b/tests/lingo/string_view.cpp @@ -312,3 +312,30 @@ LINGO_UNIT_TEST_CASE("string_view can be swapped") REQUIRE(test_string_view1 == test_string_view4); REQUIRE(test_string_view2 == test_string_view3); } + +LINGO_UNIT_TEST_CASE("string_view can be copied to an array") +{ + LINGO_UNIT_TEST_TYPEDEFS; + + const unit_type* data = lingo::test::test_string::value; + const size_type size = sizeof(lingo::test::test_string::value) / sizeof(lingo::test::test_string::value[0]) - 1; + const string_view_type source(data, size, true); + + for (auto pos_it = point_iterator_type(source); pos_it != point_iterator_type(); ++pos_it) + { + const size_type pos = pos_it.read_ptr() - source.data(); + + for (auto size_it = pos_it; size_it != point_iterator_type(); ++size_it) + { + const size_type buffer_size = (size_it.read_ptr() - source.data()) - pos; + auto buffer = std::make_unique(buffer_size); + + const size_type copied_count = source.copy(buffer.get(), buffer_size, pos); + + REQUIRE(copied_count == buffer_size); + REQUIRE(string_view_type(buffer.get(), buffer_size, false) == source.substr(pos, buffer_size)); + } + } + + REQUIRE_THROWS_AS(source.copy(nullptr, 0, size + 1), std::out_of_range); +} \ No newline at end of file