100.00% Lines (53/53) 100.00% Functions (10/10)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP 10   #ifndef BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
11   #define BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP 11   #define BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/buffers.hpp> 14   #include <boost/capy/buffers.hpp>
15   #include <boost/capy/detail/except.hpp> 15   #include <boost/capy/detail/except.hpp>
16   #include <string> 16   #include <string>
17   17  
18   namespace boost { 18   namespace boost {
19   namespace capy { 19   namespace capy {
20   20  
21 - /** A dynamic buffer using an underlying string 21 + /** A dynamic buffer backed by a `std::basic_string`.
  22 +
  23 + This adapter wraps an externally-owned string and
  24 + exposes it through the @ref DynamicBuffer interface.
  25 + Readable bytes occupy the front of the string; writable
  26 + bytes are appended by `prepare` and made readable by
  27 + `commit`.
  28 +
  29 + @note The wrapped string must outlive this adapter.
  30 + Calls to `prepare`, `commit`, and `consume`
  31 + invalidate previously returned buffer views.
  32 +
  33 + @par Thread Safety
  34 + Distinct objects: Safe.
  35 + Shared objects: Unsafe.
  36 +
  37 + @par Example
  38 + @code
  39 + std::string s;
  40 + auto buf = dynamic_buffer( s, 4096 );
  41 + auto mb = buf.prepare( 100 );
  42 + // fill mb with data...
  43 + buf.commit( 100 );
  44 + // buf.data() now has 100 readable bytes
  45 + buf.consume( 50 );
  46 + @endcode
  47 +
  48 + @tparam CharT The character type.
  49 + @tparam Traits The character traits type.
  50 + @tparam Allocator The allocator type.
  51 +
  52 + @see DynamicBuffer, string_dynamic_buffer, dynamic_buffer
22   */ 53   */
23   template< 54   template<
24   class CharT, 55   class CharT,
25   class Traits = std::char_traits<CharT>, 56   class Traits = std::char_traits<CharT>,
26   class Allocator = std::allocator<CharT>> 57   class Allocator = std::allocator<CharT>>
27   class basic_string_dynamic_buffer 58   class basic_string_dynamic_buffer
28   { 59   {
29   std::basic_string< 60   std::basic_string<
30   CharT, Traits, Allocator>* s_; 61   CharT, Traits, Allocator>* s_;
31   std::size_t max_size_; 62   std::size_t max_size_;
32   63  
33   std::size_t in_size_ = 0; 64   std::size_t in_size_ = 0;
34   std::size_t out_size_ = 0; 65   std::size_t out_size_ = 0;
35   66  
36   public: 67   public:
  68 + /// Indicates this is a DynamicBuffer adapter over external storage.
37   using is_dynamic_buffer_adapter = void; 69   using is_dynamic_buffer_adapter = void;
  70 +
  71 + /// The underlying string type.
38   using string_type = std::basic_string< 72   using string_type = std::basic_string<
39   CharT, Traits, Allocator>; 73   CharT, Traits, Allocator>;
  74 +
  75 + /// The ConstBufferSequence type for readable bytes.
40   using const_buffers_type = const_buffer; 76   using const_buffers_type = const_buffer;
  77 +
  78 + /// The MutableBufferSequence type for writable bytes.
41   using mutable_buffers_type = mutable_buffer; 79   using mutable_buffers_type = mutable_buffer;
42   80  
  81 + /// Destroy the buffer.
43   ~basic_string_dynamic_buffer() = default; 82   ~basic_string_dynamic_buffer() = default;
44   83  
45 - /** Constructor. 84 + /// Construct by moving from another buffer.
46 - */  
HITCBC 47   266 basic_string_dynamic_buffer( 85   266 basic_string_dynamic_buffer(
48   basic_string_dynamic_buffer&& other) noexcept 86   basic_string_dynamic_buffer&& other) noexcept
HITCBC 49   266 : s_(other.s_) 87   266 : s_(other.s_)
HITCBC 50   266 , max_size_(other.max_size_) 88   266 , max_size_(other.max_size_)
HITCBC 51   266 , in_size_(other.in_size_) 89   266 , in_size_(other.in_size_)
HITCBC 52   266 , out_size_(other.out_size_) 90   266 , out_size_(other.out_size_)
53   { 91   {
HITCBC 54   266 other.s_ = nullptr; 92   266 other.s_ = nullptr;
HITCBC 55   266 } 93   266 }
56   94  
57 - /** Constructor. 95 + /** Construct from an existing string.
  96 +
  97 + @param s Pointer to the string to wrap. Must
  98 + remain valid for the lifetime of this object.
  99 + @param max_size Optional upper bound on the number
  100 + of bytes the buffer may hold.
58   */ 101   */
59   explicit 102   explicit
HITCBC 60   427 basic_string_dynamic_buffer( 103   427 basic_string_dynamic_buffer(
61   string_type* s, 104   string_type* s,
62   std::size_t max_size = 105   std::size_t max_size =
63   std::size_t(-1)) noexcept 106   std::size_t(-1)) noexcept
HITCBC 64   427 : s_(s) 107   427 : s_(s)
HITCBC 65   427 , max_size_( 108   427 , max_size_(
HITCBC 66   427 max_size > s_->max_size() 109   427 max_size > s_->max_size()
HITCBC 67   427 ? s_->max_size() 110   427 ? s_->max_size()
HITCBC 68   427 : max_size) 111   427 : max_size)
69   { 112   {
HITCBC 70   427 if(s_->size() > max_size_) 113   427 if(s_->size() > max_size_)
HITCBC 71   1 s_->resize(max_size_); 114   1 s_->resize(max_size_);
HITCBC 72   427 in_size_ = s_->size(); 115   427 in_size_ = s_->size();
HITCBC 73   427 } 116   427 }
74   117  
75 - /** Assignment. 118 + /// Copy assignment is deleted.
76 - */  
77   basic_string_dynamic_buffer& operator=( 119   basic_string_dynamic_buffer& operator=(
78   basic_string_dynamic_buffer const&) = delete; 120   basic_string_dynamic_buffer const&) = delete;
79   121  
  122 + /// Return the number of readable bytes.
80   std::size_t 123   std::size_t
HITCBC 81   1280 size() const noexcept 124   1280 size() const noexcept
82   { 125   {
HITCBC 83   1280 return in_size_; 126   1280 return in_size_;
84   } 127   }
85   128  
  129 + /// Return the maximum number of bytes the buffer can hold.
86   std::size_t 130   std::size_t
HITCBC 87   410 max_size() const noexcept 131   410 max_size() const noexcept
88   { 132   {
HITCBC 89   410 return max_size_; 133   410 return max_size_;
90   } 134   }
91   135  
  136 + /// Return the number of writable bytes without reallocation.
92   std::size_t 137   std::size_t
HITCBC 93   2 capacity() const noexcept 138   2 capacity() const noexcept
94   { 139   {
HITCBC 95   2 if(s_->capacity() <= max_size_) 140   2 if(s_->capacity() <= max_size_)
HITCBC 96   1 return s_->capacity() - in_size_; 141   1 return s_->capacity() - in_size_;
HITCBC 97   1 return max_size_ - in_size_; 142   1 return max_size_ - in_size_;
98   } 143   }
99   144  
  145 + /// Return a buffer sequence representing the readable bytes.
100   const_buffers_type 146   const_buffers_type
HITCBC 101   387 data() const noexcept 147   387 data() const noexcept
102   { 148   {
HITCBC 103   387 return const_buffers_type( 149   387 return const_buffers_type(
HITCBC 104   774 s_->data(), in_size_); 150   774 s_->data(), in_size_);
105   } 151   }
106   152  
  153 + /** Prepare writable space of at least `n` bytes.
  154 +
  155 + Invalidates iterators and references returned by
  156 + previous calls to `data` and `prepare`.
  157 +
  158 + @throws std::invalid_argument if `n` exceeds
  159 + available space.
  160 +
  161 + @param n The number of bytes to prepare.
  162 +
  163 + @return A mutable buffer of exactly `n` bytes.
  164 + */
107   mutable_buffers_type 165   mutable_buffers_type
HITCBC 108   533 prepare(std::size_t n) 166   533 prepare(std::size_t n)
109   { 167   {
110   // n exceeds available space 168   // n exceeds available space
HITCBC 111   533 if(n > max_size_ - in_size_) 169   533 if(n > max_size_ - in_size_)
HITCBC 112   1 detail::throw_invalid_argument(); 170   1 detail::throw_invalid_argument();
113   171  
HITCBC 114   532 if( s_->size() < in_size_ + n) 172   532 if( s_->size() < in_size_ + n)
HITCBC 115   531 s_->resize(in_size_ + n); 173   531 s_->resize(in_size_ + n);
HITCBC 116   532 out_size_ = n; 174   532 out_size_ = n;
HITCBC 117   532 return mutable_buffers_type( 175   532 return mutable_buffers_type(
HITCBC 118   1064 &(*s_)[in_size_], out_size_); 176   1064 &(*s_)[in_size_], out_size_);
119   } 177   }
120   178  
  179 + /** Move bytes from the writable to the readable area.
  180 +
  181 + Invalidates iterators and references returned by
  182 + previous calls to `data` and `prepare`.
  183 +
  184 + @param n The number of bytes to commit. Clamped
  185 + to the size of the writable area.
  186 + */
HITCBC 121   434 void commit(std::size_t n) noexcept 187   434 void commit(std::size_t n) noexcept
122   { 188   {
HITCBC 123   434 if(n < out_size_) 189   434 if(n < out_size_)
HITCBC 124   242 in_size_ += n; 190   242 in_size_ += n;
125   else 191   else
HITCBC 126   192 in_size_ += out_size_; 192   192 in_size_ += out_size_;
HITCBC 127   434 out_size_ = 0; 193   434 out_size_ = 0;
HITCBC 128   434 s_->resize(in_size_); 194   434 s_->resize(in_size_);
HITCBC 129   434 } 195   434 }
130   196  
  197 + /** Remove bytes from the beginning of the readable area.
  198 +
  199 + Invalidates iterators and references returned by
  200 + previous calls to `data` and `prepare`.
  201 +
  202 + @param n The number of bytes to consume. Clamped
  203 + to the number of readable bytes.
  204 + */
HITCBC 131   168 void consume(std::size_t n) noexcept 205   168 void consume(std::size_t n) noexcept
132   { 206   {
HITCBC 133   168 if(n < in_size_) 207   168 if(n < in_size_)
134   { 208   {
HITCBC 135   3 s_->erase(0, n); 209   3 s_->erase(0, n);
HITCBC 136   3 in_size_ -= n; 210   3 in_size_ -= n;
137   } 211   }
138   else 212   else
139   { 213   {
HITCBC 140   165 s_->clear(); 214   165 s_->clear();
HITCBC 141   165 in_size_ = 0; 215   165 in_size_ = 0;
142   } 216   }
HITCBC 143   168 out_size_ = 0; 217   168 out_size_ = 0;
HITCBC 144   168 } 218   168 }
145   }; 219   };
146   220  
  221 + /// A dynamic buffer using `std::string`.
147   using string_dynamic_buffer = basic_string_dynamic_buffer<char>; 222   using string_dynamic_buffer = basic_string_dynamic_buffer<char>;
148   223  
149   /** Create a dynamic buffer from a string. 224   /** Create a dynamic buffer from a string.
150   225  
151   @param s The string to wrap. 226   @param s The string to wrap.
152   @param max_size Optional maximum size limit. 227   @param max_size Optional maximum size limit.
153   @return A string_dynamic_buffer wrapping the string. 228   @return A string_dynamic_buffer wrapping the string.
154   */ 229   */
155   template<class CharT, class Traits, class Allocator> 230   template<class CharT, class Traits, class Allocator>
156   basic_string_dynamic_buffer<CharT, Traits, Allocator> 231   basic_string_dynamic_buffer<CharT, Traits, Allocator>
HITCBC 157   132 dynamic_buffer( 232   132 dynamic_buffer(
158   std::basic_string<CharT, Traits, Allocator>& s, 233   std::basic_string<CharT, Traits, Allocator>& s,
159   std::size_t max_size = std::size_t(-1)) 234   std::size_t max_size = std::size_t(-1))
160   { 235   {
HITCBC 161   132 return basic_string_dynamic_buffer<CharT, Traits, Allocator>(&s, max_size); 236   132 return basic_string_dynamic_buffer<CharT, Traits, Allocator>(&s, max_size);
162   } 237   }
163   238  
164   } // capy 239   } // capy
165   } // boost 240   } // boost
166   241  
167   #endif 242   #endif