OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

blob.hpp
Go to the documentation of this file.
1 /* $Id: blob.hpp 27363 2015-08-08 13:19:38Z alberth $ */
2
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 */
9
12 #ifndef BLOB_HPP
13 #define BLOB_HPP
14
15 #include "../core/alloc_func.hpp"
16
47 class ByteBlob {
48 protected:
50 struct BlobHeader {
51 size_t items;
52 size_t capacity;
53 };
54
56 union {
57 byte *data;
58 BlobHeader *header;
59 };
60
61 private:
67 static BlobHeader hdrEmpty[];
68
69 public:
70 static const size_t tail_reserve = 4;
71 static const size_t header_size = sizeof(BlobHeader);
72
74 inline ByteBlob()
75 {
76 InitEmpty();
77 }
78
80 inline ByteBlob(const ByteBlob &src)
81 {
82 InitEmpty();
83 AppendRaw(src);
84 }
85
87 inline ByteBlob(BlobHeader * const & src)
88 {
89 assert(src != NULL);
90 header = src;
91 *const_cast<BlobHeader**>(&src) = NULL;
92 }
93
95 inline ~ByteBlob()
96 {
97 Free();
98 }
99
100 protected:
102 static inline BlobHeader *RawAlloc(size_t num_bytes)
103 {
104 return (BlobHeader*)MallocT<byte>(num_bytes);
105 }
106
111 static inline BlobHeader *Zero()
112 {
113 return const_cast<BlobHeader *>(&ByteBlob::hdrEmpty[1]);
114 }
115
117 static inline size_t AllocPolicy(size_t min_alloc)
118 {
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);
122 }
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);
126 }
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);
130 }
131 min_alloc = (min_alloc | ((1 << 20) - 1)) + 1;
132 return min_alloc;
133 }
134
136 static inline void RawFree(BlobHeader *p)
137 {
138 /* Just to silence an unsilencable GCC 4.4+ warning. */
139 assert(p != ByteBlob::hdrEmpty);
140
141 /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */
142 free(p);
143 }
144
146 inline void InitEmpty()
147 {
148 header = Zero();
149 }
150
152 inline void Init(BlobHeader *src)
153 {
154 header = &src[1];
155 }
156
158 inline BlobHeader& Hdr()
159 {
160 return *(header - 1);
161 }
162
164 inline const BlobHeader& Hdr() const
165 {
166 return *(header - 1);
167 }
168
170 inline size_t& LengthRef()
171 {
172 return Hdr().items;
173 }
174
175 public:
177 inline bool IsEmpty() const
178 {
179 return Length() == 0;
180 }
181
183 inline size_t Length() const
184 {
185 return Hdr().items;
186 }
187
189 inline size_t Capacity() const
190 {
191 return Hdr().capacity;
192 }
193
195 inline byte *Begin()
196 {
197 return data;
198 }
199
201 inline const byte *Begin() const
202 {
203 return data;
204 }
205
207 inline void Clear()
208 {
209 LengthRef() = 0;
210 }
211
213 inline void Free()
214 {
215 if (Capacity() > 0) {
216 RawFree(&Hdr());
217 InitEmpty();
218 }
219 }
220
222 inline void AppendRaw(const void *p, size_t num_bytes)
223 {
224 assert(p != NULL);
225 if (num_bytes > 0) {
226 memcpy(Append(num_bytes), p, num_bytes);
227 }
228 }
229
231 inline void AppendRaw(const ByteBlob& src)
232 {
233 if (!src.IsEmpty()) {
234 memcpy(Append(src.Length()), src.Begin(), src.Length());
235 }
236 }
237
242 inline byte *Prepare(size_t num_bytes)
243 {
244 size_t new_size = Length() + num_bytes;
245 if (new_size > Capacity()) SmartAlloc(new_size);
246 return data + Length();
247 }
248
253 inline byte *Append(size_t num_bytes)
254 {
255 byte *pNewData = Prepare(num_bytes);
256 LengthRef() += num_bytes;
257 return pNewData;
258 }
259
261 void SmartAlloc(size_t new_size)
262 {
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);
268
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);
273
274 /* copy existing data */
275 if (tmp->items != 0) {
276 memcpy(tmp + 1, data, tmp->items);
277 }
278
279 /* replace our block with new one */
280 if (Capacity() > 0) {
281 RawFree(&Hdr());
282 }
283 Init(tmp);
284 }
285
287 inline void FixTail() const
288 {
289 if (Capacity() > 0) {
290 byte *p = &data[Length()];
291 for (uint i = 0; i < tail_reserve; i++) {
292 p[i] = 0;
293 }
294 }
295 }
296 };
297
307 template <typename T>
308 class CBlobT : public ByteBlob {
309 /* make template arguments public: */
310 public:
311 typedef ByteBlob base;
312
313 static const size_t type_size = sizeof(T);
314
315 struct OnTransfer {
316 typename base::BlobHeader *header;
317
318 OnTransfer(const OnTransfer& src) : header(src.header)
319 {
320 assert(src.header != NULL);
321 *const_cast<typename base::BlobHeader**>(&src.header) = NULL;
322 }
323
324 OnTransfer(CBlobT& src) : header(src.header)
325 {
326 src.InitEmpty();
327 }
328
329 ~OnTransfer()
330 {
331 assert(header == NULL);
332 }
333 };
334
336 inline CBlobT()
337 : base()
338 {}
339
341 inline CBlobT(const OnTransfer& ot)
342 : base(ot.header)
343 {}
344
346 inline ~CBlobT()
347 {
348 Free();
349 }
350
352 inline void CheckIdx(size_t index) const
353 {
354 assert(index < Size());
355 }
356
358 inline T *Data()
359 {
360 return (T*)base::Begin();
361 }
362
364 inline const T *Data() const
365 {
366 return (const T*)base::Begin();
367 }
368
370 inline T *Data(size_t index)
371 {
372 CheckIdx(index);
373 return (Data() + index);
374 }
375
377 inline const T *Data(size_t index) const
378 {
379 CheckIdx(index);
380 return (Data() + index);
381 }
382
384 inline size_t Size() const
385 {
386 return (base::Length() / type_size);
387 }
388
390 inline size_t MaxSize() const
391 {
392 return (base::Capacity() / type_size);
393 }
394
396 inline size_t GetReserve() const
397 {
398 return ((base::Capacity() - base::Length()) / type_size);
399 }
400
402 inline T *GrowSizeNC(size_t num_items)
403 {
404 return (T*)base::Append(num_items * type_size);
405 }
406
411 inline T *MakeFreeSpace(size_t num_items)
412 {
413 return (T*)base::Prepare(num_items * type_size);
414 }
415
416 inline OnTransfer Transfer()
417 {
418 return OnTransfer(*this);
419 }
420 };
421
422
423 #endif /* BLOB_HPP */