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 FORCEINLINE ByteBlob() { InitEmpty(); } 00075 00077 FORCEINLINE ByteBlob(const ByteBlob &src) 00078 { 00079 InitEmpty(); 00080 AppendRaw(src); 00081 } 00082 00084 FORCEINLINE ByteBlob(BlobHeader * const & src) 00085 { 00086 assert(src != NULL); 00087 header = src; 00088 *const_cast<BlobHeader**>(&src) = NULL; 00089 } 00090 00092 FORCEINLINE ~ByteBlob() 00093 { 00094 Free(); 00095 } 00096 00097 protected: 00099 static FORCEINLINE BlobHeader *RawAlloc(size_t num_bytes) 00100 { 00101 return (BlobHeader*)MallocT<byte>(num_bytes); 00102 } 00103 00108 static FORCEINLINE BlobHeader *Zero() 00109 { 00110 return const_cast<BlobHeader *>(&ByteBlob::hdrEmpty[1]); 00111 } 00112 00114 static FORCEINLINE 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 FORCEINLINE 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 FORCEINLINE void InitEmpty() 00144 { 00145 header = Zero(); 00146 } 00147 00149 FORCEINLINE void Init(BlobHeader *src) 00150 { 00151 header = &src[1]; 00152 } 00153 00155 FORCEINLINE BlobHeader& Hdr() 00156 { 00157 return *(header - 1); 00158 } 00159 00161 FORCEINLINE const BlobHeader& Hdr() const 00162 { 00163 return *(header - 1); 00164 } 00165 00167 FORCEINLINE size_t& LengthRef() 00168 { 00169 return Hdr().items; 00170 } 00171 00172 public: 00174 FORCEINLINE bool IsEmpty() const 00175 { 00176 return Length() == 0; 00177 } 00178 00180 FORCEINLINE size_t Length() const 00181 { 00182 return Hdr().items; 00183 } 00184 00186 FORCEINLINE size_t Capacity() const 00187 { 00188 return Hdr().capacity; 00189 } 00190 00192 FORCEINLINE byte *Begin() 00193 { 00194 return data; 00195 } 00196 00198 FORCEINLINE const byte *Begin() const 00199 { 00200 return data; 00201 } 00202 00204 FORCEINLINE void Clear() 00205 { 00206 LengthRef() = 0; 00207 } 00208 00210 FORCEINLINE void Free() 00211 { 00212 if (Capacity() > 0) { 00213 RawFree(&Hdr()); 00214 InitEmpty(); 00215 } 00216 } 00217 00219 FORCEINLINE 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 FORCEINLINE void AppendRaw(const ByteBlob& src) 00229 { 00230 if (!src.IsEmpty()) { 00231 memcpy(Append(src.Length()), src.Begin(), src.Length()); 00232 } 00233 } 00234 00239 FORCEINLINE 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 FORCEINLINE 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 new_size = AllocPolicy(header_size + new_size + tail_reserve); 00264 00265 /* allocate new block and setup header */ 00266 BlobHeader *tmp = RawAlloc(new_size); 00267 tmp->items = Length(); 00268 tmp->capacity = new_size - (header_size + tail_reserve); 00269 00270 /* copy existing data */ 00271 if (tmp->items != 0) { 00272 memcpy(tmp + 1, data, tmp->items); 00273 } 00274 00275 /* replace our block with new one */ 00276 if (Capacity() > 0) { 00277 RawFree(&Hdr()); 00278 } 00279 Init(tmp); 00280 } 00281 00283 FORCEINLINE void FixTail() const 00284 { 00285 if (Capacity() > 0) { 00286 byte *p = &data[Length()]; 00287 for (uint i = 0; i < tail_reserve; i++) { 00288 p[i] = 0; 00289 } 00290 } 00291 } 00292 }; 00293 00303 template <typename T> 00304 class CBlobT : public ByteBlob { 00305 /* make template arguments public: */ 00306 public: 00307 typedef ByteBlob base; 00308 00309 static const size_t type_size = sizeof(T); 00310 00311 struct OnTransfer { 00312 typename base::BlobHeader *header; 00313 OnTransfer(const OnTransfer& src) : header(src.header) {assert(src.header != NULL); *const_cast<typename base::BlobHeader**>(&src.header) = NULL;} 00314 OnTransfer(CBlobT& src) : header(src.header) {src.InitEmpty();} 00315 ~OnTransfer() {assert(header == NULL);} 00316 }; 00317 00319 FORCEINLINE CBlobT() 00320 : base() 00321 {} 00322 00324 FORCEINLINE CBlobT(const OnTransfer& ot) 00325 : base(ot.header) 00326 {} 00327 00329 FORCEINLINE ~CBlobT() 00330 { 00331 Free(); 00332 } 00333 00335 FORCEINLINE void CheckIdx(size_t index) const 00336 { 00337 assert(index < Size()); 00338 } 00339 00341 FORCEINLINE T *Data() 00342 { 00343 return (T*)base::Begin(); 00344 } 00345 00347 FORCEINLINE const T *Data() const 00348 { 00349 return (const T*)base::Begin(); 00350 } 00351 00353 FORCEINLINE T *Data(size_t index) 00354 { 00355 CheckIdx(index); 00356 return (Data() + index); 00357 } 00358 00360 FORCEINLINE const T *Data(size_t index) const 00361 { 00362 CheckIdx(index); 00363 return (Data() + index); 00364 } 00365 00367 FORCEINLINE size_t Size() const 00368 { 00369 return (base::Length() / type_size); 00370 } 00371 00373 FORCEINLINE size_t MaxSize() const 00374 { 00375 return (base::Capacity() / type_size); 00376 } 00377 00379 FORCEINLINE size_t GetReserve() const 00380 { 00381 return ((base::Capacity() - base::Length()) / type_size); 00382 } 00383 00385 FORCEINLINE T *GrowSizeNC(size_t num_items) 00386 { 00387 return (T*)base::Append(num_items * type_size); 00388 } 00389 00394 FORCEINLINE T *MakeFreeSpace(size_t num_items) 00395 { 00396 return (T*)base::Prepare(num_items * type_size); 00397 } 00398 00399 FORCEINLINE OnTransfer Transfer() 00400 { 00401 return OnTransfer(*this); 00402 } 00403 }; 00404 00405 00406 #endif /* BLOB_HPP */