diff --git a/include/boost/mysql/detail/auxiliar/field_ptr.hpp.hpp b/include/boost/mysql/detail/auxiliar/field_ptr.hpp.hpp new file mode 100644 index 00000000..13f0d606 --- /dev/null +++ b/include/boost/mysql/detail/auxiliar/field_ptr.hpp.hpp @@ -0,0 +1,105 @@ +// +// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_MYSQL_DETAIL_AUXILIAR_FIELD_PTR_HPP_HPP +#define BOOST_MYSQL_DETAIL_AUXILIAR_FIELD_PTR_HPP_HPP + +#include +#include +#include +#include + +namespace boost { +namespace mysql { +namespace detail { + +class field_ptr +{ + enum class type + { + field_view, + field + }; + + type type_ {type::field_view}; + const void* ptr_ {}; + + const void* incremented(std::ptrdiff_t n) const noexcept + { + if (type_ == type::field_view) + { + return static_cast(ptr_) + n; + } + else + { + return static_cast(ptr_) + n; + } + } + field_view dereference() const noexcept + { + if (type_ == type::field_view) + { + return *static_cast(ptr_); + } + else + { + return *static_cast(ptr_); + } + } + field_ptr(type t, const void* ptr) noexcept : type_(t), ptr_(ptr) {} +public: + using value_type = field; + using reference = field_view; + using pointer = void const*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + field_ptr() noexcept = default; + explicit field_ptr(const field_view* v) noexcept : type_(type::field_view), ptr_(v) {} + explicit field_ptr(const field* v) noexcept : type_(type::field), ptr_(v) {} + + const field* as_field() const noexcept { assert(type_ == type::field); return static_cast(ptr_); } + const field_view* as_field_view() const noexcept { assert(type_ == type::field_view); return static_cast(ptr_); } + + field_ptr& operator++() noexcept { ptr_ = incremented(1); return *this; } + field_ptr operator++(int) noexcept { return field_ptr(type_, incremented(1)); } + field_ptr& operator--() noexcept { ptr_ = incremented(-1); return *this; } + field_ptr operator--(int) noexcept { return field_ptr(type_, incremented(-1)); } + field_ptr& operator+=(std::ptrdiff_t n) noexcept { ptr_ = incremented(n); return *this; } + field_ptr& operator-=(std::ptrdiff_t n) noexcept { ptr_ = incremented(-n); return *this; } + field_ptr operator+(std::ptrdiff_t n) const noexcept { return field_ptr(type_, incremented(n)); } + field_ptr operator-(std::ptrdiff_t n) const noexcept { return field_ptr(type_, incremented(-n)); } + std::ptrdiff_t operator-(const field_ptr& rhs) const noexcept + { + if (type_ == type::field) + { + return static_cast(ptr_) - static_cast(rhs.ptr_); + } + else + { + return static_cast(ptr_) - static_cast(rhs.ptr_); + } + } + field_view operator*() const noexcept { return dereference(); } + field_view operator[](std::ptrdiff_t i) const noexcept { return (*this + i).dereference(); } + bool operator==(const field_ptr& rhs) const noexcept { return type_ == rhs.type_ && ptr_ == rhs.ptr_; } + bool operator!=(const field_ptr& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const field_ptr& rhs) const noexcept { return ptr_ < rhs.ptr_; } + bool operator<=(const field_ptr& rhs) const noexcept { return ptr_ <= rhs.ptr_; } + bool operator>(const field_ptr& rhs) const noexcept { return ptr_ > rhs.ptr_; } + bool operator>=(const field_ptr& rhs) const noexcept { return ptr_ >= rhs.ptr_; } +}; + +inline field_ptr operator+(std::ptrdiff_t n, const field_ptr& ptr) noexcept { return ptr + n; } + +} +} +} + + + +#endif diff --git a/include/boost/mysql/impl/row.ipp b/include/boost/mysql/impl/row.ipp index c317fe2f..afc836f0 100644 --- a/include/boost/mysql/impl/row.ipp +++ b/include/boost/mysql/impl/row.ipp @@ -11,134 +11,64 @@ #pragma once #include +#include +#include -inline void boost::mysql::row::rebase_strings( - const char* old_buffer_base +std::vector::iterator boost::mysql::row::from_ptr( + detail::field_ptr ptr ) noexcept { - auto diff = string_buffer_.data() - old_buffer_base; - for (auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - f = field_view(boost::string_view( - str->data() + diff, - str->size() - )); - } - } + assert(ptr >= begin() && ptr <= end()); + return fields_.begin() + (begin() - ptr); } -inline void boost::mysql::row::copy_strings() -{ - // Calculate size - std::size_t size = 0; - for (const auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - size += str->size(); - } - } - - // Make space - string_buffer_.resize(size); - - // Copy the strings - std::size_t offset = 0; - for (auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - std::memcpy(string_buffer_.data() + offset, str->data(), str->size()); - f = field_view(boost::string_view(string_buffer_.data() + offset, str->size())); - offset += str->size(); - } - } -} - -inline boost::mysql::row::iterator boost::mysql::row::to_row_it( - std::vector::iterator it +boost::mysql::detail::field_ptr boost::mysql::row::to_ptr( + std::vector::iterator it ) noexcept { - return it == fields_.end() ? nullptr : fields_.data() + (it - fields_.begin()); -} - -inline std::vector::iterator boost::mysql::row::to_vector_it( - iterator it -) noexcept -{ - return it == nullptr ? fields_.end() : fields_.begin() + (it - fields_.data()); -} - -boost::mysql::row::row( - const row& other -) : - fields_(other.fields_), - string_buffer_(other.string_buffer_) -{ - rebase_strings(other.string_buffer_.data()); + return begin() + (fields_.begin() - it); } -const boost::mysql::row& boost::mysql::row::operator=( - const row& rhs -) -{ - fields_ = rhs.fields_; - string_buffer_ = rhs.string_buffer_; - rebase_strings(rhs.string_buffer_.data()); - return *this; -} - -inline boost::string_view boost::mysql::row::copy_string( - boost::string_view v -) -{ - const char* old_buffer_base = string_buffer_.data(); - std::size_t old_buffer_size = string_buffer_.size(); - string_buffer_.insert(string_buffer_.end(), v.data(), v.data() + v.size()); - if (string_buffer_.data() != old_buffer_base) - rebase_strings(old_buffer_base); - return boost::string_view(string_buffer_.data() + old_buffer_size, v.size()); -} - -inline boost::mysql::row::iterator boost::mysql::row::insert( - iterator before, +boost::mysql::row::iterator boost::mysql::row::replace( + iterator pos, field_view v ) { - const auto* str = v.if_string(); - if (str) - { - v = field_view(copy_string(*str)); - } - auto res = fields_.insert(to_vector_it(before), v); - return to_row_it(res); + assert(pos >= begin() && pos < end()); + *const_cast(pos.as_field()) = v; + return pos; } -// inline boost::mysql::row::iterator boost::mysql::row::insert( -// iterator before, -// std::initializer_list v -// ) -// { -// // Calculate the extra size required for the strings -// std::size_t new_string_size = 0; -// for (const auto& f: v) -// { -// const auto* str = f.if_string(); -// if (str) -// { -// new_string_size += str->size(); -// } -// } +boost::mysql::row::iterator boost::mysql::row::replace( + iterator first, + iterator last, + std::initializer_list v +) +{ + return replace(first, last, v.begin(), v.end()); +} -// } +template +boost::mysql::row::iterator boost::mysql::row::replace( + iterator first, + iterator last, + FwdIt other_first, + FwdIt other_last +) +{ + assert(last >= first); + assert((last - first) == std::distance(other_first, other_last)); + auto itfrom = other_first; + auto itto = first; + for (; itto != last; ++itto, ++itfrom) + { + *const_cast(itto.as_field()) = *itfrom; + } + return first; // TODO: first or last? +} #endif diff --git a/include/boost/mysql/impl/row_view.ipp b/include/boost/mysql/impl/row_view.ipp index 11f2ee8d..02710656 100644 --- a/include/boost/mysql/impl/row_view.ipp +++ b/include/boost/mysql/impl/row_view.ipp @@ -13,6 +13,17 @@ #include #include #include +#include + + +boost::mysql::field_view boost::mysql::row_view::at( + std::size_t i +) const +{ + if (i >= size_) + throw std::out_of_range("row_view::at"); + return fields_[i]; +} inline bool boost::mysql::operator==( const row_view& lhs, diff --git a/include/boost/mysql/row.hpp b/include/boost/mysql/row.hpp index 95b179b9..76807e37 100644 --- a/include/boost/mysql/row.hpp +++ b/include/boost/mysql/row.hpp @@ -10,7 +10,8 @@ #include #include -#include +#include +#include #include @@ -44,18 +45,23 @@ class row { public: row() = default; - inline row(const row&); + row(row_view r) : fields_(r.begin(), r.end()) {} + row(const row&) = default; row(row&&) = default; - inline const row& operator=(const row&); + row& operator=(const row&) = default; row& operator=(row&&) = default; ~row() = default; - using iterator = const field_view*; + using iterator = detail::field_ptr; using const_iterator = iterator; - // TODO: add other standard container members when we add field and field_view + using value_type = field; + using reference = field_view; + using const_reference = reference; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; - iterator begin() const noexcept { return fields_.data(); } - iterator end() const noexcept { return fields_.data() + fields_.size(); } + iterator begin() const noexcept { return iterator(fields_.data()); } + iterator end() const noexcept { return iterator(fields_.data() + fields_.size()); } field_view at(std::size_t i) const { return fields_.at(i); } field_view operator[](std::size_t i) const noexcept { return fields_[i]; } field_view front() const noexcept { return fields_.front(); } @@ -63,21 +69,21 @@ public: bool empty() const noexcept { return fields_.empty(); } std::size_t size() const noexcept { return fields_.size(); } - inline iterator insert(iterator before, field_view v); - inline iterator insert(iterator before, std::initializer_list v); + inline iterator insert(iterator before, field_view v) { return to_ptr(fields_.insert(from_ptr(before), v)); } + inline iterator insert(iterator before, std::initializer_list v) { return to_ptr(fields_.insert(from_ptr(before), v.begin(), v.end())); } template - iterator insert(iterator before, FwdIt first, FwdIt last); + iterator insert(iterator before, FwdIt first, FwdIt last) { return to_ptr(fields_.insert(from_ptr(before), first, last));} inline iterator replace(iterator pos, field_view v); inline iterator replace(iterator first, iterator last, std::initializer_list v); template inline iterator replace(iterator first, iterator last, FwdIt other_first, FwdIt other_last); - inline iterator erase(iterator pos); - inline iterator erase(iterator first, iterator last); + inline iterator erase(iterator pos) { return to_ptr(fields_.erase(from_ptr(pos))); } + inline iterator erase(iterator first, iterator last) { return to_ptr(fields_.erase(from_ptr(first), from_ptr(last))); } - inline void push_back(field_view v); - inline void pop_back(); + inline void push_back(field_view v) { fields_.emplace_back(v); } + inline void pop_back() { fields_.pop_back(); } operator row_view() const noexcept { @@ -93,24 +99,12 @@ public: * pointers, references and iterators to elements in [refmem row values] will be invalidated. * Any string values using the memory held by this row will also become invalid. */ - void clear() noexcept - { - fields_.clear(); - string_buffer_.clear(); - } - - // TODO: hide these - const std::vector& fields() const noexcept { return fields_; } - std::vector& values() noexcept { return fields_; } - inline void copy_strings(); + void clear() noexcept { fields_.clear(); } private: - std::vector fields_; - std::vector string_buffer_; + inline std::vector::iterator from_ptr(detail::field_ptr) noexcept; + inline detail::field_ptr to_ptr(std::vector::iterator) noexcept; - inline iterator to_row_it(std::vector::iterator it) noexcept; - inline std::vector::iterator to_vector_it(iterator it) noexcept; - inline boost::string_view copy_string(boost::string_view v); - inline void rebase_strings(const char* old_buffer_base) noexcept; + std::vector fields_; }; diff --git a/include/boost/mysql/row_view.hpp b/include/boost/mysql/row_view.hpp index 9a71193c..c33e365a 100644 --- a/include/boost/mysql/row_view.hpp +++ b/include/boost/mysql/row_view.hpp @@ -8,32 +8,41 @@ #ifndef BOOST_MYSQL_ROW_VIEW_HPP #define BOOST_MYSQL_ROW_VIEW_HPP -#include +#include #include #include +#include namespace boost { namespace mysql { + class row_view { - const field_view* fields_ {}; - std::size_t size_ {}; public: row_view() = default; - row_view(const field_view* f, std::size_t size) noexcept : fields_ {f}, size_{size} {} + row_view(const field_view* f, std::size_t size) noexcept : fields_ (f), size_(size) {} + row_view(const field* f, std::size_t size) noexcept : fields_(f), size_(size) {} - using iterator = const field_view*; + using iterator = detail::field_ptr; using const_iterator = iterator; + using value_type = field; + using reference = field_view; + using const_reference = field_view; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; iterator begin() const noexcept { return fields_; } iterator end() const noexcept { return fields_ + size_; } field_view at(std::size_t i) const; field_view operator[](std::size_t i) const noexcept { return fields_[i]; } - field_view front() const noexcept { return fields_[0]; } + field_view front() const noexcept { return *fields_; } field_view back() const noexcept { return fields_[size_ - 1]; } bool empty() const noexcept { return size_ == 0; } std::size_t size() const noexcept { return size_; } +private: + detail::field_ptr fields_ {}; + std::size_t size_ {}; };