sbo_buffer.hpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. //
  2. // Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru)
  3. //
  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)
  6. //
  7. // Official repository: https://github.com/boostorg/json
  8. //
  9. #ifndef BOOST_JSON_DETAIL_SBO_BUFFER_HPP
  10. #define BOOST_JSON_DETAIL_SBO_BUFFER_HPP
  11. #include <boost/core/detail/static_assert.hpp>
  12. #include <boost/json/detail/config.hpp>
  13. #include <boost/json/detail/except.hpp>
  14. #include <string>
  15. #include <array>
  16. namespace boost {
  17. namespace json {
  18. namespace detail {
  19. template< std::size_t N >
  20. class sbo_buffer
  21. {
  22. struct size_ptr_pair
  23. {
  24. std::size_t size;
  25. char* ptr;
  26. };
  27. BOOST_CORE_STATIC_ASSERT( N >= sizeof(size_ptr_pair) );
  28. union {
  29. std::array<char, N> buffer_;
  30. std::size_t capacity_;
  31. };
  32. char* data_ = buffer_.data();
  33. std::size_t size_ = 0;
  34. bool
  35. is_small() const noexcept
  36. {
  37. return data_ == buffer_.data();
  38. }
  39. void
  40. dispose()
  41. {
  42. if( is_small() )
  43. return;
  44. delete[] data_;
  45. #if defined(__GNUC__)
  46. # pragma GCC diagnostic push
  47. # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
  48. #endif
  49. buffer_ = {};
  50. #if defined(__GNUC__)
  51. # pragma GCC diagnostic pop
  52. #endif
  53. data_ = buffer_.data();
  54. }
  55. static constexpr
  56. std::size_t
  57. max_size() noexcept
  58. {
  59. return BOOST_JSON_MAX_STRING_SIZE;
  60. }
  61. public:
  62. sbo_buffer()
  63. : buffer_()
  64. {}
  65. sbo_buffer( sbo_buffer&& other ) noexcept
  66. : size_(other.size_)
  67. {
  68. if( other.is_small() )
  69. {
  70. buffer_ = other.buffer_;
  71. data_ = buffer_.data();
  72. }
  73. else
  74. {
  75. data_ = other.data_;
  76. other.data_ = other.buffer_.data();
  77. }
  78. BOOST_ASSERT( other.is_small() );
  79. }
  80. sbo_buffer&
  81. operator=( sbo_buffer&& other ) noexcept
  82. {
  83. if( &other == this )
  84. return this;
  85. if( other.is_small() )
  86. {
  87. buffer_ = other.buffer_;
  88. data_ = buffer_.data();
  89. }
  90. else
  91. {
  92. data_ = other.data_;
  93. other.data_ = other.buffer_.data();
  94. }
  95. size_ = other.size_;
  96. other.size_ = 0;
  97. return *this;
  98. }
  99. ~sbo_buffer()
  100. {
  101. if( !is_small() )
  102. delete[] data_;
  103. }
  104. std::size_t
  105. capacity() const noexcept
  106. {
  107. return is_small() ? buffer_.size() : capacity_;
  108. }
  109. void
  110. reset() noexcept
  111. {
  112. dispose();
  113. clear();
  114. }
  115. void
  116. clear()
  117. {
  118. size_ = 0;
  119. }
  120. void
  121. grow( std::size_t size )
  122. {
  123. if( !size )
  124. return;
  125. if( max_size() - size_ < size )
  126. {
  127. BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
  128. detail::throw_system_error( error::number_too_large, &loc );
  129. }
  130. std::size_t const old_capacity = this->capacity();
  131. std::size_t new_capacity = size_ + size;
  132. // growth factor 2
  133. if( old_capacity <= max_size() - old_capacity ) // check for overflow
  134. new_capacity = (std::max)(old_capacity * 2, new_capacity);
  135. char* new_data = new char[new_capacity];
  136. std::memcpy(new_data, data_, size_);
  137. dispose();
  138. data_ = new_data;
  139. capacity_ = new_capacity;
  140. }
  141. char*
  142. append( char const* ptr, std::size_t size )
  143. {
  144. grow(size);
  145. if(BOOST_JSON_LIKELY( size ))
  146. std::memcpy( data_ + size_, ptr, size );
  147. size_ += size;
  148. return data_;
  149. }
  150. std::size_t
  151. size() noexcept
  152. {
  153. return size_;
  154. }
  155. };
  156. } // namespace detail
  157. } // namespace json
  158. } // namespace boost
  159. #endif // BOOST_JSON_DETAIL_SBO_BUFFER_HPP