qxLib
sbo_bytes.inl
Go to the documentation of this file.
1 /**
2 
3  @file sbo_bytes.inl
4  @author Khrapov
5  @date 20.12.2025
6  @copyright © Nick Khrapov, 2025. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 template<class traits_t>
14 sbo_bytes<traits_t>::sbo_bytes(sbo_bytes&& other) noexcept
15 {
16  std::swap(m_Data, other.m_Data);
17  std::swap(m_nSize, other.m_nSize);
18  std::swap(m_nAllocatedSize, other.m_nAllocatedSize);
19 }
20 
21 template<class traits_t>
22 sbo_bytes<traits_t>::~sbo_bytes() noexcept
23 {
24  QX_STATIC_ASSERT_EQ(sizeof(sbo_bytes), nSBOSize);
25  free();
26  m_nSize = 0;
27 }
28 
29 template<class traits_t>
30 sbo_bytes<traits_t>& sbo_bytes<traits_t>::operator=(sbo_bytes&& other) noexcept
31 {
32  std::swap(m_Data, other.m_Data);
33  std::swap(m_nSize, other.m_nSize);
34  std::swap(m_nAllocatedSize, other.m_nAllocatedSize);
35  return *this;
36 }
37 
38 template<class traits_t>
39 bool sbo_bytes<traits_t>::resize(size_type nRequestedSize) noexcept
40 {
41  bool bRet = true;
42  if (nRequestedSize > capacity())
43  {
44  bRet = reallocate_to_big(get_new_size(nRequestedSize));
45  }
46  else
47  {
48  if constexpr (bShrinkToFitWhenSmall)
49  if (!is_small() && nRequestedSize <= nBufferSize)
50  reallocate_to_small(nRequestedSize);
51  }
52 
53  if (bRet) [[likely]]
54  m_nSize = nRequestedSize;
55 
56  return bRet;
57 }
58 
59 template<class traits_t>
60 bool sbo_bytes<traits_t>::reserve(size_type nRequestedSize) noexcept
61 {
62  if (nRequestedSize <= capacity())
63  return true;
64 
65  return reallocate_to_big(get_new_size(nRequestedSize));
66 }
67 
68 template<class traits_t>
70 {
71  if (m_nSize <= nBufferSize)
72  {
73  if (!is_small())
74  reallocate_to_small(m_nSize);
75 
76  return true;
77  }
78  else
79  {
80  return reallocate_to_big(m_nSize);
81  }
82 }
83 
84 template<class traits_t>
86 {
87  if (!is_small())
88  {
89  std::free(m_Data.pData);
90  m_Data.buffer = buffer_type();
91  }
92 
93  m_nAllocatedSize = 0;
94 }
95 
96 template<class traits_t>
98 {
99  if (is_small())
100  return m_Data.buffer.data();
101  else
102  return m_Data.pData;
103 }
104 
105 template<class traits_t>
106 QX_FORCE_INLINE const std::byte* sbo_bytes<traits_t>::data() const noexcept
107 {
108  return QX_CONST_CAST_THIS()->data();
109 }
110 
111 template<class traits_t>
112 QX_FORCE_INLINE typename sbo_bytes<traits_t>::size_type sbo_bytes<traits_t>::size() const noexcept
113 {
114  return m_nSize;
115 }
116 
117 template<class traits_t>
118 QX_FORCE_INLINE typename sbo_bytes<traits_t>::size_type sbo_bytes<traits_t>::capacity() const noexcept
119 {
120  return is_small() ? nBufferSize : m_nAllocatedSize;
121 }
122 
123 template<class traits_t>
125 {
126  return m_nAllocatedSize == 0;
127 }
128 
129 template<class traits_t>
130 bool sbo_bytes<traits_t>::reallocate_to_big(size_type nNewSize) noexcept
131 {
132  const bool bSmallAtStart = is_small();
133 
134  if (void* pNewBlock = std::realloc(bSmallAtStart ? nullptr : m_Data.pData, nNewSize)) [[likely]]
135  {
136  if constexpr (bPreserveContents)
137  if (bSmallAtStart)
138  std::memcpy(pNewBlock, m_Data.buffer.data(), std::min(nNewSize, capacity()));
139 
140  m_Data.pData = static_cast<std::byte*>(pNewBlock);
141  m_nAllocatedSize = nNewSize;
142  return true;
143  }
144  else
145  {
146  return false;
147  }
148 }
149 
150 template<class traits_t>
151 void sbo_bytes<traits_t>::reallocate_to_small(size_type nNewSize) noexcept
152 {
153  if constexpr (bPreserveContents)
154  {
155  buffer_type buff;
156  std::memcpy(buff.data(), m_Data.pData, std::min(nNewSize, capacity()));
157  free();
158  m_Data.buffer = buff;
159  }
160  else
161  {
162  free();
163  }
164 }
165 
166 template<class traits_t>
167 QX_FORCE_INLINE sbo_bytes<traits_t>::size_type sbo_bytes<traits_t>::get_new_size(
168  size_type nRequestedSize) const noexcept
169 {
170  const size_t nCapacity = capacity();
171  const size_t nAdvancedGrowth = traits_type::growth_strategy(nCapacity);
172  size_t nNewSize = nRequestedSize;
173  if (nCapacity < nRequestedSize && nRequestedSize < nAdvancedGrowth)
174  nNewSize = nAdvancedGrowth;
175 
176  return nNewSize;
177 }
178 
179 } // namespace qx
A type erased small buffer object that works with raw data.
Definition: sbo_bytes.h:30
QX_FORCE_INLINE size_type capacity() const noexcept
Get SBO capacity (bytes)
Definition: sbo_bytes.inl:118
QX_FORCE_INLINE bool is_small() const noexcept
Is the SBO small and fits into the local buffer.
Definition: sbo_bytes.inl:124
bool reserve(size_type nRequestedSize) noexcept
Make sure the capacity is at least nRequestedSize and allocate memory if needed, but do not change th...
Definition: sbo_bytes.inl:60
bool shrink_to_fit() noexcept
Reduce capacity to fit the size, possibly moving data to the internal buffer if the size becomes smal...
Definition: sbo_bytes.inl:69
void free() noexcept
Free allocated memory.
Definition: sbo_bytes.inl:85
QX_FORCE_INLINE std::byte * data() noexcept
Get SBO data: from a buffer or from a heap.
Definition: sbo_bytes.inl:97
QX_FORCE_INLINE size_type size() const noexcept
Get SBO size (bytes)
Definition: sbo_bytes.inl:112
bool resize(size_type nRequestedSize) noexcept
Make sure the capacity is at least nRequestedSize and allocate memory if needed, and change the size ...
Definition: sbo_bytes.inl:39
#define QX_FORCE_INLINE
Make this function forcefully inlined (except for QX_DEBUG build)
Definition: common.h:92
#define QX_CONST_CAST_THIS()
This macro is made for situations where you have a const method, and you need exactly the same method...
Definition: common.h:63