OpenTTD
blob.hpp
Go to the documentation of this file.
1 /* $Id$ */
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;
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 */