OpenTTD
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;
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 */