(svn r28004) -Update from Eints:
[openttd.git] / src / misc / blob.hpp
blobb1a5b667df03c05b0cc788439c7e7cd8401928a5
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file blob.hpp Support for storing random binary data. */
12 #ifndef BLOB_HPP
13 #define BLOB_HPP
15 #include "../core/alloc_func.hpp"
17 /**
18 * Base class for simple binary blobs.
19 * Item is byte.
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).
47 class ByteBlob {
48 protected:
49 /** header of the allocated memory block */
50 struct BlobHeader {
51 size_t items; ///< actual blob size in bytes
52 size_t capacity; ///< maximum (allocated) size in bytes
55 /** type used as class member */
56 union {
57 byte *data; ///< ptr to the first byte of data
58 BlobHeader *header; ///< ptr just after the BlobHeader holding items and capacity
61 private:
62 /**
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[];
69 public:
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 */
74 inline ByteBlob()
76 InitEmpty();
79 /** copy constructor */
80 inline ByteBlob(const ByteBlob &src)
82 InitEmpty();
83 AppendRaw(src);
86 /** move constructor - take ownership of blob data */
87 inline ByteBlob(BlobHeader * const & src)
89 assert(src != NULL);
90 header = src;
91 *const_cast<BlobHeader**>(&src) = NULL;
94 /** destructor */
95 inline ~ByteBlob()
97 Free();
100 protected:
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;
132 return min_alloc;
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. */
142 free(p);
145 /** initialize the empty blob */
146 inline void InitEmpty()
148 header = Zero();
151 /** initialize blob by attaching it to the given header followed by data */
152 inline void Init(BlobHeader *src)
154 header = &src[1];
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()
172 return Hdr().items;
175 public:
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
185 return Hdr().items;
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 */
195 inline byte *Begin()
197 return data;
200 /** return pointer to the first byte of data - const version */
201 inline const byte *Begin() const
203 return data;
206 /** invalidate blob's data - doesn't free buffer */
207 inline void Clear()
209 LengthRef() = 0;
212 /** free the blob's memory */
213 inline void Free()
215 if (Capacity() > 0) {
216 RawFree(&Hdr());
217 InitEmpty();
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 != NULL);
225 if (num_bytes > 0) {
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;
257 return pNewData;
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) {
281 RawFree(&Hdr());
283 Init(tmp);
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++) {
292 p[i] = 0;
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: */
310 public:
311 typedef ByteBlob base;
313 static const size_t type_size = sizeof(T);
315 struct OnTransfer {
316 typename base::BlobHeader *header;
318 OnTransfer(const OnTransfer& src) : header(src.header)
320 assert(src.header != NULL);
321 *const_cast<typename base::BlobHeader**>(&src.header) = NULL;
324 OnTransfer(CBlobT& src) : header(src.header)
326 src.InitEmpty();
329 ~OnTransfer()
331 assert(header == NULL);
335 /** Default constructor - makes new Blob ready to accept any data */
336 inline CBlobT()
337 : base()
340 /** Take ownership constructor */
341 inline CBlobT(const OnTransfer& ot)
342 : base(ot.header)
345 /** Destructor - ensures that allocated memory (if any) is freed */
346 inline ~CBlobT()
348 Free();
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 */
358 inline T *Data()
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)
372 CheckIdx(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
379 CheckIdx(index);
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 */