00001 /* $Id$ */ 00002 00003 /* 00004 * This file is part of OpenTTD. 00005 * 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. 00006 * 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. 00007 * 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/>. 00008 */ 00009 00012 #ifndef BLOB_HPP 00013 #define BLOB_HPP 00014 00015 #include "../core/alloc_func.hpp" 00016 00047 class ByteBlob { 00048 protected: 00050 struct BlobHeader { 00051 size_t items; 00052 size_t capacity; 00053 }; 00054 00056 union { 00057 byte *data; 00058 BlobHeader *header; 00059 }; 00060 00061 private: 00067 static BlobHeader hdrEmpty[]; 00068 00069 public: 00070 static const size_t tail_reserve = 4; 00071 static const size_t header_size = sizeof(BlobHeader); 00072 00074 inline ByteBlob() { InitEmpty(); } 00075 00077 inline ByteBlob(const ByteBlob &src) 00078 { 00079 InitEmpty(); 00080 AppendRaw(src); 00081 } 00082 00084 inline ByteBlob(BlobHeader * const & src) 00085 { 00086 assert(src != NULL); 00087 header = src; 00088 *const_cast<BlobHeader**>(&src) = NULL; 00089 } 00090 00092 inline ~ByteBlob() 00093 { 00094 Free(); 00095 } 00096 00097 protected: 00099 static inline BlobHeader *RawAlloc(size_t num_bytes) 00100 { 00101 return (BlobHeader*)MallocT<byte>(num_bytes); 00102 } 00103 00108 static inline BlobHeader *Zero() 00109 { 00110 return const_cast<BlobHeader *>(&ByteBlob::hdrEmpty[1]); 00111 } 00112 00114 static inline size_t AllocPolicy(size_t min_alloc) 00115 { 00116 if (min_alloc < (1 << 9)) { 00117 if (min_alloc < (1 << 5)) return (1 << 5); 00118 return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9); 00119 } 00120 if (min_alloc < (1 << 15)) { 00121 if (min_alloc < (1 << 11)) return (1 << 11); 00122 return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15); 00123 } 00124 if (min_alloc < (1 << 20)) { 00125 if (min_alloc < (1 << 17)) return (1 << 17); 00126 return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20); 00127 } 00128 min_alloc = (min_alloc | ((1 << 20) - 1)) + 1; 00129 return min_alloc; 00130 } 00131 00133 static inline void RawFree(BlobHeader *p) 00134 { 00135 /* Just to silence an unsilencable GCC 4.4+ warning. */ 00136 assert(p != ByteBlob::hdrEmpty); 00137 00138 /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */ 00139 free(p); 00140 } 00141 00143 inline void InitEmpty() 00144 { 00145 header = Zero(); 00146 } 00147 00149 inline void Init(BlobHeader *src) 00150 { 00151 header = &src[1]; 00152 } 00153 00155 inline BlobHeader& Hdr() 00156 { 00157 return *(header - 1); 00158 } 00159 00161 inline const BlobHeader& Hdr() const 00162 { 00163 return *(header - 1); 00164 } 00165 00167 inline size_t& LengthRef() 00168 { 00169 return Hdr().items; 00170 } 00171 00172 public: 00174 inline bool IsEmpty() const 00175 { 00176 return Length() == 0; 00177 } 00178 00180 inline size_t Length() const 00181 { 00182 return Hdr().items; 00183 } 00184 00186 inline size_t Capacity() const 00187 { 00188 return Hdr().capacity; 00189 } 00190 00192 inline byte *Begin() 00193 { 00194 return data; 00195 } 00196 00198 inline const byte *Begin() const 00199 { 00200 return data; 00201 } 00202 00204 inline void Clear() 00205 { 00206 LengthRef() = 0; 00207 } 00208 00210 inline void Free() 00211 { 00212 if (Capacity() > 0) { 00213 RawFree(&Hdr()); 00214 InitEmpty(); 00215 } 00216 } 00217 00219 inline void AppendRaw(const void *p, size_t num_bytes) 00220 { 00221 assert(p != NULL); 00222 if (num_bytes > 0) { 00223 memcpy(Append(num_bytes), p, num_bytes); 00224 } 00225 } 00226 00228 inline void AppendRaw(const ByteBlob& src) 00229 { 00230 if (!src.IsEmpty()) { 00231 memcpy(Append(src.Length()), src.Begin(), src.Length()); 00232 } 00233 } 00234 00239 inline byte *Prepare(size_t num_bytes) 00240 { 00241 size_t new_size = Length() + num_bytes; 00242 if (new_size > Capacity()) SmartAlloc(new_size); 00243 return data + Length(); 00244 } 00245 00250 inline byte *Append(size_t num_bytes) 00251 { 00252 byte *pNewData = Prepare(num_bytes); 00253 LengthRef() += num_bytes; 00254 return pNewData; 00255 } 00256 00258 void SmartAlloc(size_t new_size) 00259 { 00260 if (Capacity() >= new_size) return; 00261 /* calculate minimum block size we need to allocate 00262 * and ask allocation policy for some reasonable block size */ 00263 assert(new_size < SIZE_MAX - header_size - tail_reserve); 00264 new_size = AllocPolicy(header_size + new_size + tail_reserve); 00265 00266 /* allocate new block and setup header */ 00267 BlobHeader *tmp = RawAlloc(new_size); 00268 tmp->items = Length(); 00269 tmp->capacity = new_size - (header_size + tail_reserve); 00270 00271 /* copy existing data */ 00272 if (tmp->items != 0) { 00273 memcpy(tmp + 1, data, tmp->items); 00274 } 00275 00276 /* replace our block with new one */ 00277 if (Capacity() > 0) { 00278 RawFree(&Hdr()); 00279 } 00280 Init(tmp); 00281 } 00282 00284 inline void FixTail() const 00285 { 00286 if (Capacity() > 0) { 00287 byte *p = &data[Length()]; 00288 for (uint i = 0; i < tail_reserve; i++) { 00289 p[i] = 0; 00290 } 00291 } 00292 } 00293 }; 00294 00304 template <typename T> 00305 class CBlobT : public ByteBlob { 00306 /* make template arguments public: */ 00307 public: 00308 typedef ByteBlob base; 00309 00310 static const size_t type_size = sizeof(T); 00311 00312 struct OnTransfer { 00313 typename base::BlobHeader *header; 00314 OnTransfer(const OnTransfer& src) : header(src.header) {assert(src.header != NULL); *const_cast<typename base::BlobHeader**>(&src.header) = NULL;} 00315 OnTransfer(CBlobT& src) : header(src.header) {src.InitEmpty();} 00316 ~OnTransfer() {assert(header == NULL);} 00317 }; 00318 00320 inline CBlobT() 00321 : base() 00322 {} 00323 00325 inline CBlobT(const OnTransfer& ot) 00326 : base(ot.header) 00327 {} 00328 00330 inline ~CBlobT() 00331 { 00332 Free(); 00333 } 00334 00336 inline void CheckIdx(size_t index) const 00337 { 00338 assert(index < Size()); 00339 } 00340 00342 inline T *Data() 00343 { 00344 return (T*)base::Begin(); 00345 } 00346 00348 inline const T *Data() const 00349 { 00350 return (const T*)base::Begin(); 00351 } 00352 00354 inline T *Data(size_t index) 00355 { 00356 CheckIdx(index); 00357 return (Data() + index); 00358 } 00359 00361 inline const T *Data(size_t index) const 00362 { 00363 CheckIdx(index); 00364 return (Data() + index); 00365 } 00366 00368 inline size_t Size() const 00369 { 00370 return (base::Length() / type_size); 00371 } 00372 00374 inline size_t MaxSize() const 00375 { 00376 return (base::Capacity() / type_size); 00377 } 00378 00380 inline size_t GetReserve() const 00381 { 00382 return ((base::Capacity() - base::Length()) / type_size); 00383 } 00384 00386 inline T *GrowSizeNC(size_t num_items) 00387 { 00388 return (T*)base::Append(num_items * type_size); 00389 } 00390 00395 inline T *MakeFreeSpace(size_t num_items) 00396 { 00397 return (T*)base::Prepare(num_items * type_size); 00398 } 00399 00400 inline OnTransfer Transfer() 00401 { 00402 return OnTransfer(*this); 00403 } 00404 }; 00405 00406 00407 #endif /* BLOB_HPP */