1 /* $Id: blob.hpp 24900 2013-01-08 22:46:42Z planetmaker $ */
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file blob.hpp Support for storing random binary data. */
15 #include "../core/alloc_func.hpp"
18 * Base class for simple binary blobs.
20 * The word 'simple' means:
21 * - no configurable allocator type (always made from heap)
22 * - no smart deallocation - deallocation must be called from the same
23 * module (DLL) where the blob was allocated
24 * - no configurable allocation policy (how big blocks should be allocated)
25 * - no extra ownership policy (i.e. 'copy on write') when blob is copied
26 * - no thread synchronization at all
28 * Internal member layout:
29 * 1. The only class member is pointer to the first item (see union).
30 * 2. Allocated block contains the blob header (see BlobHeader) followed by the raw byte data.
31 * Always, when it allocates memory the allocated size is:
32 * sizeof(BlobHeader) + <data capacity>
33 * 3. Two 'virtual' members (items and capacity) are stored in the BlobHeader at beginning
34 * of the allocated block.
35 * 4. The pointer of the union pobsize_ts behind the header (to the first data byte).
36 * When memory block is allocated, the sizeof(BlobHeader) it added to it.
37 * 5. Benefits of this layout:
38 * - items are accessed in the simplest possible way - just dereferencing the pointer,
39 * which is good for performance (assuming that data are accessed most often).
40 * - sizeof(blob) is the same as the size of any other pointer
41 * 6. Drawbacks of this layout:
42 * - the fact that a pointer to the allocated block is adjusted by sizeof(BlobHeader) before
43 * it is stored can lead to several confusions:
44 * - it is not a common pattern so the implementation code is bit harder to read.
45 * - valgrind may generate a warning that the allocated block is lost (not accessible).
49 /** header of the allocated memory block */
51 size_t items
; ///< actual blob size in bytes
52 size_t capacity
; ///< maximum (allocated) size in bytes
55 /** type used as class member */
57 byte
*data
; ///< ptr to the first byte of data
58 BlobHeader
*header
; ///< ptr just after the BlobHeader holding items and capacity
63 * Just to silence an unsilencable GCC 4.4+ warning
64 * Note: This cannot be 'const' as we do a lot of 'hdrEmpty[0]->items += 0;' and 'hdrEmpty[0]->capacity += 0;'
65 * after const_casting.
67 static BlobHeader hdrEmpty
[];
70 static const size_t tail_reserve
= 4; ///< four extra bytes will be always allocated and zeroed at the end
71 static const size_t header_size
= sizeof(BlobHeader
);
73 /** default constructor - initializes empty blob */
79 /** copy constructor */
80 inline ByteBlob(const ByteBlob
&src
)
86 /** move constructor - take ownership of blob data */
87 inline ByteBlob(BlobHeader
* const & src
)
89 assert(src
!= nullptr);
91 *const_cast<BlobHeader
**>(&src
) = nullptr;
101 /** all allocation should happen here */
102 static inline BlobHeader
*RawAlloc(size_t num_bytes
)
104 return (BlobHeader
*)MallocT
<byte
>(num_bytes
);
108 * Return header pointer to the static BlobHeader with
109 * both items and capacity containing zero
111 static inline BlobHeader
*Zero()
113 return const_cast<BlobHeader
*>(&ByteBlob::hdrEmpty
[1]);
116 /** simple allocation policy - can be optimized later */
117 static inline size_t AllocPolicy(size_t min_alloc
)
119 if (min_alloc
< (1 << 9)) {
120 if (min_alloc
< (1 << 5)) return (1 << 5);
121 return (min_alloc
< (1 << 7)) ? (1 << 7) : (1 << 9);
123 if (min_alloc
< (1 << 15)) {
124 if (min_alloc
< (1 << 11)) return (1 << 11);
125 return (min_alloc
< (1 << 13)) ? (1 << 13) : (1 << 15);
127 if (min_alloc
< (1 << 20)) {
128 if (min_alloc
< (1 << 17)) return (1 << 17);
129 return (min_alloc
< (1 << 19)) ? (1 << 19) : (1 << 20);
131 min_alloc
= (min_alloc
| ((1 << 20) - 1)) + 1;
135 /** all deallocations should happen here */
136 static inline void RawFree(BlobHeader
*p
)
138 /* Just to silence an unsilencable GCC 4.4+ warning. */
139 assert(p
!= ByteBlob::hdrEmpty
);
141 /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */
145 /** initialize the empty blob */
146 inline void InitEmpty()
151 /** initialize blob by attaching it to the given header followed by data */
152 inline void Init(BlobHeader
*src
)
157 /** blob header accessor - use it rather than using the pointer arithmetics directly - non-const version */
158 inline BlobHeader
& Hdr()
160 return *(header
- 1);
163 /** blob header accessor - use it rather than using the pointer arithmetics directly - const version */
164 inline const BlobHeader
& Hdr() const
166 return *(header
- 1);
169 /** return reference to the actual blob size - used when the size needs to be modified */
170 inline size_t& LengthRef()
176 /** return true if blob doesn't contain valid data */
177 inline bool IsEmpty() const
179 return Length() == 0;
182 /** return the number of valid data bytes in the blob */
183 inline size_t Length() const
188 /** return the current blob capacity in bytes */
189 inline size_t Capacity() const
191 return Hdr().capacity
;
194 /** return pointer to the first byte of data - non-const version */
200 /** return pointer to the first byte of data - const version */
201 inline const byte
*Begin() const
206 /** invalidate blob's data - doesn't free buffer */
212 /** free the blob's memory */
215 if (Capacity() > 0) {
221 /** append new bytes at the end of existing data bytes - reallocates if necessary */
222 inline void AppendRaw(const void *p
, size_t num_bytes
)
224 assert(p
!= nullptr);
226 memcpy(Append(num_bytes
), p
, num_bytes
);
230 /** append bytes from given source blob to the end of existing data bytes - reallocates if necessary */
231 inline void AppendRaw(const ByteBlob
& src
)
233 if (!src
.IsEmpty()) {
234 memcpy(Append(src
.Length()), src
.Begin(), src
.Length());
239 * Reallocate if there is no free space for num_bytes bytes.
240 * @return pointer to the new data to be added
242 inline byte
*Prepare(size_t num_bytes
)
244 size_t new_size
= Length() + num_bytes
;
245 if (new_size
> Capacity()) SmartAlloc(new_size
);
246 return data
+ Length();
250 * Increase Length() by num_bytes.
251 * @return pointer to the new data added
253 inline byte
*Append(size_t num_bytes
)
255 byte
*pNewData
= Prepare(num_bytes
);
256 LengthRef() += num_bytes
;
260 /** reallocate blob data if needed */
261 void SmartAlloc(size_t new_size
)
263 if (Capacity() >= new_size
) return;
264 /* calculate minimum block size we need to allocate
265 * and ask allocation policy for some reasonable block size */
266 assert(new_size
< SIZE_MAX
- header_size
- tail_reserve
);
267 new_size
= AllocPolicy(header_size
+ new_size
+ tail_reserve
);
269 /* allocate new block and setup header */
270 BlobHeader
*tmp
= RawAlloc(new_size
);
271 tmp
->items
= Length();
272 tmp
->capacity
= new_size
- (header_size
+ tail_reserve
);
274 /* copy existing data */
275 if (tmp
->items
!= 0) {
276 memcpy(tmp
+ 1, data
, tmp
->items
);
279 /* replace our block with new one */
280 if (Capacity() > 0) {
286 /** fixing the four bytes at the end of blob data - useful when blob is used to hold string */
287 inline void FixTail() const
289 if (Capacity() > 0) {
290 byte
*p
= &data
[Length()];
291 for (uint i
= 0; i
< tail_reserve
; i
++) {
299 * Blob - simple dynamic T array. T (template argument) is a placeholder for any type.
300 * T can be any integral type, pointer, or structure. Using Blob instead of just plain C array
301 * simplifies the resource management in several ways:
302 * 1. When adding new item(s) it automatically grows capacity if needed.
303 * 2. When variable of type Blob comes out of scope it automatically frees the data buffer.
304 * 3. Takes care about the actual data size (number of used items).
305 * 4. Dynamically constructs only used items (as opposite of static array which constructs all items)
307 template <typename T
>
308 class CBlobT
: public ByteBlob
{
309 /* make template arguments public: */
311 typedef ByteBlob base
;
313 static const size_t type_size
= sizeof(T
);
316 typename
base::BlobHeader
*header
;
318 OnTransfer(const OnTransfer
& src
) : header(src
.header
)
320 assert(src
.header
!= nullptr);
321 *const_cast<typename
base::BlobHeader
**>(&src
.header
) = nullptr;
324 OnTransfer(CBlobT
& src
) : header(src
.header
)
331 assert(header
== nullptr);
335 /** Default constructor - makes new Blob ready to accept any data */
340 /** Take ownership constructor */
341 inline CBlobT(const OnTransfer
& ot
)
345 /** Destructor - ensures that allocated memory (if any) is freed */
351 /** Check the validity of item index (only in debug mode) */
352 inline void CheckIdx(size_t index
) const
354 assert(index
< Size());
357 /** Return pointer to the first data item - non-const version */
360 return (T
*)base::Begin();
363 /** Return pointer to the first data item - const version */
364 inline const T
*Data() const
366 return (const T
*)base::Begin();
369 /** Return pointer to the index-th data item - non-const version */
370 inline T
*Data(size_t index
)
373 return (Data() + index
);
376 /** Return pointer to the index-th data item - const version */
377 inline const T
*Data(size_t index
) const
380 return (Data() + index
);
383 /** Return number of items in the Blob */
384 inline size_t Size() const
386 return (base::Length() / type_size
);
389 /** Return total number of items that can fit in the Blob without buffer reallocation */
390 inline size_t MaxSize() const
392 return (base::Capacity() / type_size
);
395 /** Return number of additional items that can fit in the Blob without buffer reallocation */
396 inline size_t GetReserve() const
398 return ((base::Capacity() - base::Length()) / type_size
);
401 /** Grow number of data items in Blob by given number - doesn't construct items */
402 inline T
*GrowSizeNC(size_t num_items
)
404 return (T
*)base::Append(num_items
* type_size
);
408 * Ensures that given number of items can be added to the end of Blob. Returns pointer to the
409 * first free (unused) item
411 inline T
*MakeFreeSpace(size_t num_items
)
413 return (T
*)base::Prepare(num_items
* type_size
);
416 inline OnTransfer
Transfer()
418 return OnTransfer(*this);
423 #endif /* BLOB_HPP */