diff --git a/src/query/command3.h b/src/query/command3.h index fdcf4dd..5057281 100644 --- a/src/query/command3.h +++ b/src/query/command3.h @@ -156,6 +156,66 @@ namespace ts { std::vector flags{}; }; + class command_builder_bulk { + template + friend class command_builder_impl; + public: + inline void put(const std::string_view& key, const std::string_view& value) { + size_t begin, end; + if(impl::value_raw_impl(this->bulk, key, begin, &end)) { + std::string result{}; + result.reserve(this->bulk.size()); + + result.append(this->bulk, 0, begin - key.size() - 1); /* key incl = */ + result.append(this->bulk, end + 1); /* get rid of the space */ + this->bulk = result; + } + this->impl_put_unchecked(key, value); + } + + template ::value || std::is_same::value), int> = 1> + inline void put(const std::string_view& key, const T& value) { + static_assert(converter::supported, "Target type isn't supported!"); + static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); + auto data = converter::to_string(value); + this->put(key, std::string_view{data}); + } + + /* directly puts data without checking for duplicates */ + inline void put_unchecked(const std::string_view& key, const std::string_view& value) { + this->impl_put_unchecked(key, value); + } + + inline void put_unchecked(const std::string_view& key, const std::string& value) { + this->put_unchecked(key, std::string_view{value}); + } + + template ::value || std::is_same::value), int> = 1> + inline void put_unchecked(const std::string_view& key, const T& value) { + static_assert(converter::supported, "Target type isn't supported!"); + static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); + auto data = converter::to_string(value); + this->put_unchecked(key, std::string_view{data}); + } + private: + bool& flag_changed; + std::string& bulk; + explicit command_builder_bulk(bool& change_flag, std::string& bulk) : flag_changed{change_flag}, bulk{bulk} {} + + void impl_put_unchecked(const std::string_view& key, const std::string_view& value) { + auto escaped_value = ts::query::escape(std::string{value}); + + this->bulk.reserve(this->bulk.length() + key.size() + escaped_value.size() + 2); + this->bulk.append(key); + if(!escaped_value.empty()) { + this->bulk.append("="); + this->bulk.append(escaped_value); + } + this->bulk.append(" "); + this->flag_changed = true; + } + }; + template > class command_builder_impl { public: @@ -169,7 +229,7 @@ namespace ts { } inline std::string build(bool with_empty = false) const { - if(this->builded.has_value()) + if(this->builded.has_value() && !this->flag_changed) return this->builded.value(); std::string result{}; @@ -189,74 +249,35 @@ namespace ts { this->builded = result.substr(0, result.length() - 1); else this->builded = result; + this->flag_changed = false; return this->builded.value(); } inline void reserve_bulks(size_t count) { this->bulks.reserve(count); } - inline void put(size_t index, const std::string_view& key, const std::string_view& value) { + [[nodiscard]] inline command_builder_bulk bulk(size_t index) { while(this->bulks.size() <= index) this->bulks.emplace_back("").reserve(expected_bulk_size); - auto& data = this->bulks[index]; - size_t begin, end; - if(impl::value_raw_impl(data, key, begin, &end)) { - std::string result{}; - result.reserve(data.size()); - - result.append(data, 0, begin - key.size() - 1); /* key incl = */ - result.append(data, end + 1); /* get rid of the space */ - data = result; - } - this->impl_put_unchecked(data, index, key, value); + return command_builder_bulk{this->flag_changed, this->bulks[index]}; } - template ::value || std::is_same::value), int> = 1> + template inline void put(size_t index, const std::string_view& key, const T& value) { - static_assert(converter::supported, "Target type isn't supported!"); - static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); - auto data = converter::to_string(value); - this->put(index, key, std::string_view{data}); + this->bulk(index).put(key, value); } /* directly puts data without checking for duplicates */ - inline void put_unchecked(size_t index, const std::string_view& key, const std::string_view& value) { - while(this->bulks.size() <= index) - this->bulks.emplace_back("").reserve(expected_bulk_size); - - this->impl_put_unchecked(this->bulks[index], index, key, value); - } - - inline void put_unchecked(size_t index, const std::string_view& key, const std::string& value) { - this->put_unchecked(index, key, std::string_view{value}); - } - - template ::value || std::is_same::value), int> = 1> + template inline void put_unchecked(size_t index, const std::string_view& key, const T& value) { - static_assert(converter::supported, "Target type isn't supported!"); - static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); - auto data = converter::to_string(value); - this->put_unchecked(index, key, std::string_view{data}); + this->bulk(index).put_unchecked(key, value); } private: - command_builder_impl(size_t expected, size_t identifier, typename vector_t::iterator begin, typename vector_t::iterator end) : expected_bulk_size{expected}, _identifier{identifier}, bulks{begin, end} {} - - void impl_put_unchecked(std::string& data, size_t index, const std::string_view& key, const std::string_view& value) { - auto escaped_value = ts::query::escape(std::string{value}); - - data.reserve(data.length() + key.size() + escaped_value.size() + 2); - data.append(key); - if(!escaped_value.empty()) { - data.append("="); - data.append(escaped_value); - } - data.append(" "); - - this->builded.reset(); - } + command_builder_impl(size_t expected, std::string identifier, typename vector_t::iterator begin, typename vector_t::iterator end) : expected_bulk_size{expected}, _identifier{std::move(identifier)}, bulks{begin, end} {} const size_t expected_bulk_size; const std::string _identifier; + mutable bool flag_changed{false}; mutable std::optional builded{}; vector_t bulks{}; };