OpenTTD: src/misc/blob.hpp Source File

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 24900 2013-01-08 22:46:42Z planetmaker $ */
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() { InitEmpty(); }
75
77 inline ByteBlob(const ByteBlob &src)
78 {
79 InitEmpty();
80 AppendRaw(src);
81 }
82
84 inline ByteBlob(BlobHeader * const & src)
85 {
86 assert(src != NULL);
87 header = src;
88 *const_cast<BlobHeader**>(&src) = NULL;
89 }
90
92 inline ~ByteBlob()
93 {
94 Free();
95 }
96
97 protected:
99 static inline BlobHeader *RawAlloc(size_t num_bytes)
100 {
101 return (BlobHeader*)MallocT<byte>(num_bytes);
102 }
103
108 static inline BlobHeader *Zero()
109 {
110 return const_cast<BlobHeader *>(&ByteBlob::hdrEmpty[1]);
111 }
112
114 static inline size_t AllocPolicy(size_t min_alloc)
115 {
116 if (min_alloc < (1 << 9)) {
117 if (min_alloc < (1 << 5)) return (1 << 5);
118 return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9);
119 }
120 if (min_alloc < (1 << 15)) {
121 if (min_alloc < (1 << 11)) return (1 << 11);
122 return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15);
123 }
124 if (min_alloc < (1 << 20)) {
125 if (min_alloc < (1 << 17)) return (1 << 17);
126 return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20);
127 }
128 min_alloc = (min_alloc | ((1 << 20) - 1)) + 1;
129 return min_alloc;
130 }
131
133 static inline void RawFree(BlobHeader *p)
134 {
135 /* Just to silence an unsilencable GCC 4.4+ warning. */
136 assert(p != ByteBlob::hdrEmpty);
137
138 /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */
139 free(p);
140 }
141
143 inline void InitEmpty()
144 {
145 header = Zero();
146 }
147
149 inline void Init(BlobHeader *src)
150 {
151 header = &src[1];
152 }
153
155 inline BlobHeader& Hdr()
156 {
157 return *(header - 1);
158 }
159
161 inline const BlobHeader& Hdr() const
162 {
163 return *(header - 1);
164 }
165
167 inline size_t& LengthRef()
168 {
169 return Hdr().items;
170 }
171
172 public:
174 inline bool IsEmpty() const
175 {
176 return Length() == 0;
177 }
178
180 inline size_t Length() const
181 {
182 return Hdr().items;
183 }
184
186 inline size_t Capacity() const
187 {
188 return Hdr().capacity;
189 }
190
192 inline byte *Begin()
193 {
194 return data;
195 }
196
198 inline const byte *Begin() const
199 {
200 return data;
201 }
202
204 inline void Clear()
205 {
206 LengthRef() = 0;
207 }
208
210 inline void Free()
211 {
212 if (Capacity() > 0) {
213 RawFree(&Hdr());
214 InitEmpty();
215 }
216 }
217
219 inline void AppendRaw(const void *p, size_t num_bytes)
220 {
221 assert(p != NULL);
222 if (num_bytes > 0) {
223 memcpy(Append(num_bytes), p, num_bytes);
224 }
225 }
226
228 inline void AppendRaw(const ByteBlob& src)
229 {
230 if (!src.IsEmpty()) {
231 memcpy(Append(src.Length()), src.Begin(), src.Length());
232 }
233 }
234
239 inline byte *Prepare(size_t num_bytes)
240 {
241 size_t new_size = Length() + num_bytes;
242 if (new_size > Capacity()) SmartAlloc(new_size);
243 return data + Length();
244 }
245
250 inline byte *Append(size_t num_bytes)
251 {
252 byte *pNewData = Prepare(num_bytes);
253 LengthRef() += num_bytes;
254 return pNewData;
255 }
256
258 void SmartAlloc(size_t new_size)
259 {
260 if (Capacity() >= new_size) return;
261 /* calculate minimum block size we need to allocate
262 * and ask allocation policy for some reasonable block size */
263 assert(new_size < SIZE_MAX - header_size - tail_reserve);
264 new_size = AllocPolicy(header_size + new_size + tail_reserve);
265
266 /* allocate new block and setup header */
267 BlobHeader *tmp = RawAlloc(new_size);
268 tmp->items = Length();
269 tmp->capacity = new_size - (header_size + tail_reserve);
270
271 /* copy existing data */
272 if (tmp->items != 0) {
273 memcpy(tmp + 1, data, tmp->items);
274 }
275
276 /* replace our block with new one */
277 if (Capacity() > 0) {
278 RawFree(&Hdr());
279 }
280 Init(tmp);
281 }
282
284 inline void FixTail() const
285 {
286 if (Capacity() > 0) {
287 byte *p = &data[Length()];
288 for (uint i = 0; i < tail_reserve; i++) {
289 p[i] = 0;
290 }
291 }
292 }
293 };
294
304 template <typename T>
305 class CBlobT : public ByteBlob {
306 /* make template arguments public: */
307 public:
308 typedef ByteBlob base;
309
310 static const size_t type_size = sizeof(T);
311
312 struct OnTransfer {
313 typename base::BlobHeader *header;
314 OnTransfer(const OnTransfer& src) : header(src.header) {assert(src.header != NULL); *const_cast<typename base::BlobHeader**>(&src.header) = NULL;}
315 OnTransfer(CBlobT& src) : header(src.header) {src.InitEmpty();}
316 ~OnTransfer() {assert(header == NULL);}
317 };
318
320 inline CBlobT()
321 : base()
322 {}
323
325 inline CBlobT(const OnTransfer& ot)
326 : base(ot.header)
327 {}
328
330 inline ~CBlobT()
331 {
332 Free();
333 }
334
336 inline void CheckIdx(size_t index) const
337 {
338 assert(index < Size());
339 }
340
342 inline T *Data()
343 {
344 return (T*)base::Begin();
345 }
346
348 inline const T *Data() const
349 {
350 return (const T*)base::Begin();
351 }
352
354 inline T *Data(size_t index)
355 {
356 CheckIdx(index);
357 return (Data() + index);
358 }
359
361 inline const T *Data(size_t index) const
362 {
363 CheckIdx(index);
364 return (Data() + index);
365 }
366
368 inline size_t Size() const
369 {
370 return (base::Length() / type_size);
371 }
372
374 inline size_t MaxSize() const
375 {
376 return (base::Capacity() / type_size);
377 }
378
380 inline size_t GetReserve() const
381 {
382 return ((base::Capacity() - base::Length()) / type_size);
383 }
384
386 inline T *GrowSizeNC(size_t num_items)
387 {
388 return (T*)base::Append(num_items * type_size);
389 }
390
395 inline T *MakeFreeSpace(size_t num_items)
396 {
397 return (T*)base::Prepare(num_items * type_size);
398 }
399
400 inline OnTransfer Transfer()
401 {
402 return OnTransfer(*this);
403 }
404 };
405
406
407 #endif /* BLOB_HPP */