libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
stack_string_impl.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2013 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE {
20 
21 template<unsigned int BuffN, class charT, class traits>
22 inline constexpr void
23 basic_stack_string<BuffN, charT, traits>::buffer_type::copy(native_databus_type const *src) noexcept(true) {
24  assert(std::strlen(reinterpret_cast<value_type const *>(src))<small_string_max_size);
25  // Unroll the copy in the hope that the compiler will notice the sequence of copies and optimize it.
26 // For comparative testing for my HFT series of talks.
27  unrolled_op_t::result([src, this](std::size_t i) {fast_copy_values[i]=src[i];});
28 // std::memcpy(small_basic_stack_string, src, std::strlen(reinterpret_cast<value_type const *>(src)));
29 }
30 
31 template<unsigned int BuffN, class charT, class traits>
32 template<std::size_t SrcSz>
33 inline constexpr void
34 basic_stack_string<BuffN, charT, traits>::buffer_type::copy(value_type const (& src)[SrcSz]) noexcept(true) {
35  static_assert(SrcSz<=small_string_max_size, "Passed array must be smaller than the buffer size.");
36 // For comparative testing for my HFT series of talks.
37  memcpy_opt(src, small_basic_stack_string);
38 // std::memcpy(small_basic_stack_string, src, SrcSz);
39 }
40 
41 template<unsigned int BuffN, class charT, class traits>
42 inline constexpr void
43 basic_stack_string<BuffN, charT, traits>::buffer_type::copy(ensure_char_ptr_argument_aligned src) noexcept(true) {
44  assert(std::strlen(src)<small_string_max_size);
45  native_databus_type const *punned_src=reinterpret_cast<native_databus_type const *>(src);
46  copy(punned_src);
47 }
48 
49 template<unsigned int BuffN, class charT, class traits>
50 inline void
51 basic_stack_string<BuffN, charT, traits>::buffer_type::copy(size_type cap, const_pointer b, const_pointer e, size_type offset) noexcept(true) {
52  if (LIKELY(cap<small_string_max_size)) {
53  // TODO ideally unroll this.
54  std::memcpy(small_basic_stack_string+offset, b, std::distance(b, e));
55  } else {
56  std::memcpy(heap+offset, b, std::distance(b, e));
57  }
58 }
59 
60 template<unsigned int BuffN, class charT, class traits>
61 inline void
62 basic_stack_string<BuffN, charT, traits>::buffer_type::fill_n(size_type cap, size_type offset, size_type n, value_type i) noexcept(true) {
63  if (LIKELY(cap<small_string_max_size)) {
64  // TODO ideally unroll this.
65  memset(small_basic_stack_string+offset, i, n);
66  } else {
67  memset(heap+offset, i, n);
68  }
69 }
70 
71 template<unsigned int BuffN, class charT, class traits>
72 inline void
73 basic_stack_string<BuffN, charT, traits>::buffer_type::ctor(size_type cap, ensure_char_ptr_argument_aligned src) noexcept(false) {
74  if (LIKELY(cap<small_string_max_size)) {
75  std::memcpy(small_basic_stack_string, src, cap);
76  } else {
77  auto ptr=std::get_temporary_buffer<value_type>(cap);
78  if (LIKELY(ptr.first)) {
79  assert(static_cast<size_type>(ptr.second)>=cap);
80  heap=ptr.first;
81  std::memcpy(heap, src, cap);
82  } else {
83  throw std::bad_alloc();
84  }
85  }
86 }
87 
88 template<unsigned int BuffN, class charT, class traits>
89 template<std::size_t SrcSz>
90 inline void
91 basic_stack_string<BuffN, charT, traits>::buffer_type::ctor(value_type const (& src)[SrcSz]) noexcept(SrcSz<=small_string_max_size) {
92  if constexpr (SrcSz<=small_string_max_size) {
93  copy<SrcSz>(src);
94  } else {
95  auto ptr=std::get_temporary_buffer<value_type>(SrcSz);
96  if (LIKELY(ptr.first)) {
97  using dest_type=value_type [SrcSz];
98  assert(static_cast<size_type>(ptr.second)>=SrcSz);
99  heap=ptr.first;
100 // For comparative testing for my HFT series of talks.
101  memcpy_opt(src, reinterpret_cast<dest_type &>(*heap));
102 // std::memcpy(reinterpret_cast<dest_type &>(*heap), src, SrcSz);
103  } else {
104  throw std::bad_alloc();
105  }
106  }
107 }
108 
109 template<unsigned int BuffN, class charT, class traits>
110 inline void
111 basic_stack_string<BuffN, charT, traits>::buffer_type::cctor(size_type cap, buffer_type const &b) noexcept(false) {
112  if (LIKELY(cap<small_string_max_size)) {
113  unrolled_op_t::result([&b, this](std::size_t i) {fast_copy_values[i]=b.fast_copy_values[i];});
114  } else {
115  auto ptr=std::get_temporary_buffer<value_type>(cap);
116  if (LIKELY(ptr.first)) {
117  assert(static_cast<size_type>(ptr.second)<=cap);
118  heap=ptr.first;
119  std::memcpy(heap, b.heap, cap);
120  } else {
121  throw std::bad_alloc();
122  }
123  }
124 }
125 
126 template<unsigned int BuffN, class charT, class traits>
127 inline void
128 basic_stack_string<BuffN, charT, traits>::buffer_type::swap(buffer_type &buff) noexcept(true) {
129  // Unroll the swap in the hope that the compiler will notice the sequence of copies and optimize it.
130  unrolled_op_t::result([this, &buff](std::size_t i) {std::swap(fast_copy_values[i], buff.fast_copy_values[i]);});
131 }
132 
133 template<unsigned int BuffN, class charT, class traits>
134 inline void
135 basic_stack_string<BuffN, charT, traits>::buffer_type::move(size_type cap, typename iterator_traits::difference_type f, typename iterator_traits::difference_type l, size_type n) noexcept(true) {
136  if (LIKELY(cap<small_string_max_size)) {
137  // TODO ideally unroll this.
138  memmove(small_basic_stack_string+f, small_basic_stack_string+l, n);
139  } else {
140  memmove(heap+f, heap+l, n);
141  }
142 }
143 
144 template<unsigned int BuffN, class charT, class traits>
145 constexpr inline
146 basic_stack_string<BuffN, charT, traits>::basic_stack_string() noexcept(true)
147 : size_(), capacity_(), buffer() {
148 }
149 
150 template<unsigned int BuffN, class charT, class traits>
151 inline
152 basic_stack_string<BuffN, charT, traits>::basic_stack_string(ensure_char_ptr_argument_aligned src, std::size_t sz) noexcept(false) {
153  if (LIKELY(src!=nullptr)) {
154  size_=sz;
155  capacity_=size_+1;
156  buffer.ctor(capacity_, src);
157  } else {
158  throw exception("nullptr input.");
159  }
160  assert(size_<capacity_);
161 }
162 
163 template<unsigned int BuffN, class charT, class traits>
164 template<std::size_t SrcSz>
165 inline
166 basic_stack_string<BuffN, charT, traits>::basic_stack_string(value_type const (& src)[SrcSz]) noexcept(SrcSz<=small_string_max_size)
167 : size_(SrcSz-1), capacity_(size_+1) {
168  buffer.template ctor<SrcSz>(src);
169  assert(size_<capacity_);
170 }
171 
172 template<unsigned int BuffN, class charT, class traits>
173 inline
174 basic_stack_string<BuffN, charT, traits>::basic_stack_string(basic_stack_string const &str) noexcept(false)
175 : size_(str.size_), capacity_() {
176  if (LIKELY(size_)) {
177  capacity_=size_+1;
178  buffer.cctor(capacity_, str.buffer);
179  assert(size_<capacity_);
180  }
181 }
182 
183 template<unsigned int BuffN, class charT, class traits>
184 inline
185 basic_stack_string<BuffN, charT, traits>::basic_stack_string(basic_stack_string &&str) noexcept(true)
186 : size_(str.size_), capacity_(str.capacity_), buffer(str.buffer) {
187  str.size_=0;
188  str.capacity_=0;
189  str.buffer.heap=nullptr;
190 }
191 
192 template<unsigned int BuffN, class charT, class traits>
193 inline
194 basic_stack_string<BuffN, charT, traits>::~basic_stack_string() noexcept(true) {
195 // For comparative testing for my HFT series of talks.
196  // Although this is UNLIKELY, DO NOT use UNLIKELY nor LIKELY here: it can give a 40% performance drop in some tests!!!
197 // if (LIKELY(capacity_>=small_string_max_size)) {
198  if (capacity_>=small_string_max_size) {
199  capacity_=size_type();
200  std::return_temporary_buffer(buffer.heap);
201  }
202 }
203 
204 template<unsigned int BuffN, class charT, class traits>
205 inline void
206 basic_stack_string<BuffN, charT, traits>::swap(basic_stack_string &str) noexcept(true) {
207  std::swap(size_, str.size_);
208  std::swap(capacity_, str.capacity_);
209  buffer.swap(str.buffer);
210 }
211 
212 template<unsigned int BuffN, class charT, class traits>
213 inline basic_stack_string<BuffN, charT, traits> &
214 basic_stack_string<BuffN, charT, traits>::operator=(basic_stack_string const &str) noexcept(false) {
215  basic_stack_string tmp(str);
216  swap(tmp);
217  return *this;
218 }
219 
220 template<unsigned int BuffN, class charT, class traits>
221 inline basic_stack_string<BuffN, charT, traits> &
222 basic_stack_string<BuffN, charT, traits>::operator=(basic_stack_string &&str) noexcept(true) {
223  swap(str);
224  return *this;
225 }
226 
227 template<unsigned int BuffN, class charT, class traits>
228 inline typename basic_stack_string<BuffN, charT, traits>::iterator
229 basic_stack_string<BuffN, charT, traits>::begin() noexcept(true) {
230  return iterator(capacity_<small_string_max_size ? buffer.small_basic_stack_string : buffer.heap);
231 }
232 
233 template<unsigned int BuffN, class charT, class traits>
234 inline typename basic_stack_string<BuffN, charT, traits>::const_iterator
235 basic_stack_string<BuffN, charT, traits>::begin() const noexcept(true) {
236  return const_iterator(capacity_<small_string_max_size ? buffer.small_basic_stack_string : buffer.heap);
237 }
238 
239 template<unsigned int BuffN, class charT, class traits>
240 inline typename basic_stack_string<BuffN, charT, traits>::iterator
241 basic_stack_string<BuffN, charT, traits>::end() noexcept(true) {
242  return iterator((capacity_<small_string_max_size ? buffer.small_basic_stack_string : buffer.heap)+size_);
243 }
244 
245 template<unsigned int BuffN, class charT, class traits>
246 inline typename basic_stack_string<BuffN, charT, traits>::const_iterator
247 basic_stack_string<BuffN, charT, traits>::end() const noexcept(true) {
248  return const_iterator((capacity_<small_string_max_size ? buffer.small_basic_stack_string : buffer.heap)+size_);
249 }
250 
251 template<unsigned int BuffN, class charT, class traits>
252 inline constexpr bool
253 basic_stack_string<BuffN, charT, traits>::operator==(basic_stack_string const &s) const noexcept(true) {
254  return size()==s.size() && memcmp(begin(), s.begin(), size());
255 }
256 
257 template<unsigned int BuffN, class charT, class traits>
258 inline constexpr bool
259 basic_stack_string<BuffN, charT, traits>::operator!=(basic_stack_string const &s) const noexcept(true) {
260  return !(*this==s);
261 }
262 
263 template<unsigned int BuffN, class charT, class traits>
264 inline constexpr typename basic_stack_string<BuffN, charT, traits>::size_type
265 basic_stack_string<BuffN, charT, traits>::size() const noexcept(true) {
266  return size_;
267 }
268 
269 template<unsigned int BuffN, class charT, class traits>
270 inline void
271 basic_stack_string<BuffN, charT, traits>::reserve(size_type s) noexcept(false) {
272  if (LIKELY(s<max_size())) {
273  // Do we need to grow?
274  if (UNLIKELY(s>capacity_)) {
275  // Are we a small basic_stack_string?
276  if (LIKELY(capacity_<small_string_max_size)) {
277  // Reserve within small basic_stack_string range?
278  if (LIKELY(s<small_string_max_size)) {
279  capacity_=s;
280  return;
281  }
282  // Need to switch to big basic_stack_string mode.
283  buffer_type tmp;
284  tmp.copy(buffer.fast_copy_values);
285  auto ptr=std::get_temporary_buffer<value_type>(s);
286  if (LIKELY(ptr.first)) {
287  assert(static_cast<size_type>(ptr.second)<=s);
288  buffer.heap=ptr.first;
289  } else {
290  throw std::bad_alloc();
291  }
292  std::memcpy(buffer.heap, tmp.small_basic_stack_string, size_+1);
293  } else {
294  // Already a big basic_stack_string. Grow the buffer.
295  // Throwing operations first.
296  auto ptr=std::get_temporary_buffer<value_type>(s);
297  if (LIKELY(ptr.first)) {
298  assert(static_cast<size_type>(ptr.second)<=s);
299  } else {
300  throw std::bad_alloc();
301  }
302  std::memcpy(ptr.first, buffer.heap, size_+1);
303  std::swap(buffer.heap, ptr.first);
304  std::return_temporary_buffer(ptr.first);
305  }
306  capacity_=s;
307  }
308  assert(size_<capacity_);
309  }
310 }
311 
312 template<unsigned int BuffN, class charT, class traits>
313 inline void
314 basic_stack_string<BuffN, charT, traits>::resize(size_type s) noexcept(false) {
315  reserve(s+1);
316  size_=s;
317  *end()=value_type();
318  assert(size_<capacity_);
319 }
320 
321 template<unsigned int BuffN, class charT, class traits>
322 inline void
323 basic_stack_string<BuffN, charT, traits>::resize(size_type s, value_type i) noexcept(false) {
324  reserve(s+1);
325  if (UNLIKELY(s>size_)) {
326  assert(s<=capacity_);
327  buffer.fill_n(capacity_, size_, s-size_, i);
328  }
329  size_=s;
330  *end()=value_type();
331  assert(size_<capacity_);
332 }
333 
334 template<unsigned int BuffN, class charT, class traits>
335 inline void
336 basic_stack_string<BuffN, charT, traits>::clear() noexcept(true) {
337  size_=size_type();
338  *end()=value_type();
339 }
340 
341 template<unsigned int BuffN, class charT, class traits>
342 inline constexpr bool
343 basic_stack_string<BuffN, charT, traits>::empty() const noexcept(true) {
344  return size()==size_type();
345 }
346 
347 template<unsigned int BuffN, class charT, class traits>
348 inline typename basic_stack_string<BuffN, charT, traits>::reference
349 basic_stack_string<BuffN, charT, traits>::operator[](size_type p) noexcept(true) {
350  return *std::next(begin(), p);
351 }
352 
353 template<unsigned int BuffN, class charT, class traits>
354 inline typename basic_stack_string<BuffN, charT, traits>::const_reference
355 basic_stack_string<BuffN, charT, traits>::operator[](size_type p) const noexcept(true) {
356  return *std::next(begin(), p);
357 }
358 
359 template<unsigned int BuffN, class charT, class traits>
360 inline void
361 basic_stack_string<BuffN, charT, traits>::push_back(value_type c) noexcept(false) {
362  resize(size_+1, c);
363 }
364 
365 template<unsigned int BuffN, class charT, class traits>
366 inline typename basic_stack_string<BuffN, charT, traits>::iterator
367 basic_stack_string<BuffN, charT, traits>::insert(iterator p, const_iterator b, const_iterator e) noexcept(false) {
368  if (LIKELY(b!=e)) {
369  assert(static_cast<size_type>(std::distance(begin(), p))<=size_);
370  assert(static_cast<size_type>(std::distance(p, end()))<=size_);
371  // Iterators may be invalidated.
372  const typename iterator_traits::difference_type size_str_ins=std::distance(b, e);
373  const typename iterator_traits::difference_type dist_in_str=std::distance(begin(), p);
374  reserve(size_+size_str_ins+1);
375  if (LIKELY(capacity_<small_string_max_size)) {
376  // TODO ideally unroll this.
377  memmove(buffer.small_basic_stack_string+dist_in_str+size_str_ins, buffer.small_basic_stack_string+dist_in_str, size_-dist_in_str);
378  std::memcpy(buffer.small_basic_stack_string+dist_in_str, b, size_str_ins);
379  size_+=size_str_ins;
380  *end()=value_type();
381  assert(size_<=capacity_);
382  return p;
383  } else {
384  memmove(buffer.heap+dist_in_str+size_str_ins, buffer.heap+dist_in_str, size_-dist_in_str);
385  std::memcpy(buffer.heap+dist_in_str, b, size_str_ins);
386  size_+=size_str_ins;
387  *end()=value_type();
388  assert(size_<=capacity_);
389  return std::next(begin()+dist_in_str-1);
390  }
391  } else {
392  return p;
393  }
394 }
395 
396 template<unsigned int BuffN, class charT, class traits>
397 inline typename basic_stack_string<BuffN, charT, traits>::iterator
398 basic_stack_string<BuffN, charT, traits>::erase(const_iterator b, const_iterator e) noexcept(true) {
399  if (LIKELY(static_cast<size_type>(std::distance(b, e))<=size_)) {
400  assert(static_cast<size_type>(std::distance(const_iterator(begin()), b))<=size_);
401  assert(static_cast<size_type>(std::distance(b, const_iterator(end())))<=size_);
402  assert(static_cast<size_type>(std::distance(const_iterator(begin()), e))<=size_);
403  assert(static_cast<size_type>(std::distance(e, const_iterator(end())))<=size_);
404  // Iterators may be invalidated.
405  const typename iterator_traits::difference_type first=std::distance(const_iterator(begin()), b);
406  const typename iterator_traits::difference_type last=std::distance(const_iterator(begin()), e);
407  buffer.move(capacity_, first, last, size_-last);
408  size_-=last-first;
409  *end()=value_type();
410  assert(size_<=capacity_);
411  return begin()+first;
412  } else {
413  return end();
414  }
415 }
416 
417 template<unsigned int BuffN, class charT, class traits>
418 inline basic_stack_string<BuffN, charT, traits> &
419 basic_stack_string<BuffN, charT, traits>::replace(iterator b, iterator e, const_iterator src_b, const_iterator src_e) noexcept(false) {
420  // The easy way to do this would be to erase(), then insert(), but it would have excessive copies.
421  if (LIKELY(src_b!=src_e)) {
422  if (UNLIKELY(b==e)) {
423  // Equivalent to insert().
424  insert(b, src_b, src_e);
425  } else {
426  assert(static_cast<size_type>(std::distance(begin(), b))<=size_);
427  assert(static_cast<size_type>(std::distance(b, end()))<=size_);
428  assert(static_cast<size_type>(std::distance(begin(), e))<=size_);
429  assert(static_cast<size_type>(std::distance(e, end()))<=size_);
430  // Iterators may be invalidated.
431  const typename iterator_traits::difference_type first=std::distance(begin(), b);
432  const typename iterator_traits::difference_type last=std::distance(begin(), e);
433  const typename iterator_traits::difference_type replace_str_sz=std::distance(src_b, src_e);
434  if (LIKELY((last-first)>=replace_str_sz)) {
435  // We're shrinking, so no issues with copying the overlapping end range.
436  if (LIKELY(capacity_<small_string_max_size)) {
437  // TODO ideally unroll this.
438  std::memcpy(buffer.small_basic_stack_string+first, src_b, replace_str_sz);
439  memmove(buffer.small_basic_stack_string+first+replace_str_sz, buffer.small_basic_stack_string+last, size_-last);
440  } else {
441  std::memcpy(buffer.heap+first, src_b, replace_str_sz);
442  memmove(buffer.heap+first+replace_str_sz, buffer.heap+last, size_-last);
443  }
444  size_-=(last-first)-replace_str_sz;
445  } else {
446  reserve(size_+(replace_str_sz-(last-first))+1);
447  if (LIKELY(capacity_<small_string_max_size)) {
448  // TODO ideally unroll this.
449  std::copy_backward(buffer.small_basic_stack_string+last, buffer.small_basic_stack_string+size_, buffer.small_basic_stack_string+first+replace_str_sz+size_-last);
450  std::memcpy(buffer.small_basic_stack_string+first, src_b, replace_str_sz);
451  } else {
452  std::copy_backward(buffer.heap+last, buffer.heap+size_, buffer.heap+first+replace_str_sz+size_-last);
453  std::memcpy(buffer.heap+first, src_b, replace_str_sz);
454  }
455  size_+=replace_str_sz-(last-first);
456  }
457  *end()=value_type();
458  }
459  } else {
460  // Equivalent to erase().
461  erase(b, e);
462  }
463  assert(size_<=capacity_);
464  return *this;
465 }
466 
467 template<unsigned int BuffN, class charT, class traits> inline std::basic_ostream<charT, traits> &
468 operator<<(std::basic_ostream<charT, traits> &os, basic_stack_string<BuffN, charT, traits> const &s) noexcept(false) {
469  os.write(s.begin(), s.size());
470  return os;
471 }
472 
473 template<unsigned int BuffN, class charT, class traits> inline std::basic_istream<charT, traits> &
474 operator>>(std::basic_istream<charT, traits> &is, basic_stack_string<BuffN, charT, traits> &s) noexcept(false) {
475  typename std::basic_istream<charT, traits>::int_type ret;
476  while ((ret=is.get())!=traits::eof()) {
477  s.push_back(static_cast<typename basic_stack_string<BuffN, charT, traits>::value_type>(ret));
478  }
479  return is;
480 }
481 
482 } }