00001 /* $Id: blob.hpp 21060 2010-10-30 17:51:07Z alberth $ */ 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 #include "../core/mem_func.hpp" 00017 #include <new> 00018 00049 class ByteBlob { 00050 protected: 00052 struct BlobHeader { 00053 size_t items; 00054 size_t capacity; 00055 }; 00056 00058 union { 00059 byte *data; 00060 BlobHeader *header; 00061 }; 00062 00063 private: 00069 static BlobHeader hdrEmpty[]; 00070 00071 public: 00072 static const size_t tail_reserve = 4; 00073 static const size_t header_size = sizeof(BlobHeader); 00074 00076 FORCEINLINE ByteBlob() { InitEmpty(); } 00077 00079 FORCEINLINE ByteBlob(const ByteBlob &src) 00080 { 00081 InitEmpty(); 00082 AppendRaw(src); 00083 } 00084 00086 FORCEINLINE ByteBlob(BlobHeader * const & src) 00087 { 00088 assert(src != NULL); 00089 header = src; 00090 *const_cast<BlobHeader**>(&src) = NULL; 00091 } 00092 00094 FORCEINLINE ~ByteBlob() 00095 { 00096 Free(); 00097 } 00098 00099 protected: 00101 static FORCEINLINE BlobHeader *RawAlloc(size_t num_bytes) 00102 { 00103 return (BlobHeader*)MallocT<byte>(num_bytes); 00104 } 00105 00110 static FORCEINLINE BlobHeader *Zero() 00111 { 00112 return const_cast<BlobHeader *>(&ByteBlob::hdrEmpty[1]); 00113 } 00114 00116 static FORCEINLINE size_t AllocPolicy(size_t min_alloc) 00117 { 00118 if (min_alloc < (1 << 9)) { 00119 if (min_alloc < (1 << 5)) return (1 << 5); 00120 return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9); 00121 } 00122 if (min_alloc < (1 << 15)) { 00123 if (min_alloc < (1 << 11)) return (1 << 11); 00124 return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15); 00125 } 00126 if (min_alloc < (1 << 20)) { 00127 if (min_alloc < (1 << 17)) return (1 << 17); 00128 return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20); 00129 } 00130 min_alloc = (min_alloc | ((1 << 20) - 1)) + 1; 00131 return min_alloc; 00132 } 00133 00135 static FORCEINLINE void RawFree(BlobHeader *p) 00136 { 00137 /* Just to silence an unsilencable GCC 4.4+ warning. */ 00138 assert(p != ByteBlob::hdrEmpty); 00139 00140 /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */ 00141 free(p); 00142 } 00143 00145 FORCEINLINE void InitEmpty() 00146 { 00147 header = Zero(); 00148 } 00149 00151 FORCEINLINE void Init(BlobHeader *src) 00152 { 00153 header = &src[1]; 00154 } 00155 00157 FORCEINLINE BlobHeader& Hdr() 00158 { 00159 return *(header - 1); 00160 } 00161 00163 FORCEINLINE const BlobHeader& Hdr() const 00164 { 00165 return *(header - 1); 00166 } 00167 00169 FORCEINLINE size_t& LengthRef() 00170 { 00171 return Hdr().items; 00172 } 00173 00174 public: 00176 FORCEINLINE bool IsEmpty() const 00177 { 00178 return Length() == 0; 00179 } 00180 00182 FORCEINLINE size_t Length() const 00183 { 00184 return Hdr().items; 00185 } 00186 00188 FORCEINLINE size_t Capacity() const 00189 { 00190 return Hdr().capacity; 00191 } 00192 00194 FORCEINLINE byte *Begin() 00195 { 00196 return data; 00197 } 00198 00200 FORCEINLINE const byte *Begin() const 00201 { 00202 return data; 00203 } 00204 00206 FORCEINLINE void Clear() 00207 { 00208 LengthRef() = 0; 00209 } 00210 00212 FORCEINLINE void Free() 00213 { 00214 if (Capacity() > 0) { 00215 RawFree(&Hdr()); 00216 InitEmpty(); 00217 } 00218 } 00219 00221 FORCEINLINE void AppendRaw(const void *p, size_t num_bytes) 00222 { 00223 assert(p != NULL); 00224 if (num_bytes > 0) { 00225 memcpy(Append(num_bytes), p, num_bytes); 00226 } 00227 } 00228 00230 FORCEINLINE void AppendRaw(const ByteBlob& src) 00231 { 00232 if (!src.IsEmpty()) { 00233 memcpy(Append(src.Length()), src.Begin(), src.Length()); 00234 } 00235 } 00236 00241 FORCEINLINE byte *Prepare(size_t num_bytes) 00242 { 00243 size_t new_size = Length() + num_bytes; 00244 if (new_size > Capacity()) SmartAlloc(new_size); 00245 return data + Length(); 00246 } 00247 00252 FORCEINLINE byte *Append(size_t num_bytes) 00253 { 00254 byte *pNewData = Prepare(num_bytes); 00255 LengthRef() += num_bytes; 00256 return pNewData; 00257 } 00258 00260 void SmartAlloc(size_t new_size) 00261 { 00262 if (Capacity() >= new_size) return; 00263 /* calculate minimum block size we need to allocate 00264 * and ask allocation policy for some reasonable block size */ 00265 new_size = AllocPolicy(header_size + new_size + tail_reserve); 00266 00267 /* allocate new block and setup header */ 00268 BlobHeader *tmp = RawAlloc(new_size); 00269 tmp->items = Length(); 00270 tmp->capacity = new_size - (header_size + tail_reserve); 00271 00272 /* copy existing data */ 00273 if (tmp->items != 0) { 00274 memcpy(tmp + 1, data, tmp->items); 00275 } 00276 00277 /* replace our block with new one */ 00278 if (Capacity() > 0) { 00279 RawFree(&Hdr()); 00280 } 00281 Init(tmp); 00282 } 00283 00285 FORCEINLINE void FixTail() const 00286 { 00287 if (Capacity() > 0) { 00288 byte *p = &data[Length()]; 00289 for (uint i = 0; i < tail_reserve; i++) { 00290 p[i] = 0; 00291 } 00292 } 00293 } 00294 }; 00295 00305 template <typename T> 00306 class CBlobT : public ByteBlob { 00307 /* make template arguments public: */ 00308 public: 00309 typedef ByteBlob base; 00310 00311 static const size_t type_size = sizeof(T); 00312 00313 struct OnTransfer { 00314 typename base::BlobHeader *header; 00315 OnTransfer(const OnTransfer& src) : header(src.header) {assert(src.header != NULL); *const_cast<typename base::BlobHeader**>(&src.header) = NULL;} 00316 OnTransfer(CBlobT& src) : header(src.header) {src.InitEmpty();} 00317 ~OnTransfer() {assert(header == NULL);} 00318 }; 00319 00321 FORCEINLINE CBlobT() 00322 : base() 00323 {} 00324 00326 FORCEINLINE CBlobT(const OnTransfer& ot) 00327 : base(ot.header) 00328 {} 00329 00331 FORCEINLINE ~CBlobT() 00332 { 00333 Free(); 00334 } 00335 00337 FORCEINLINE void CheckIdx(size_t index) const 00338 { 00339 assert(index < Size()); 00340 } 00341 00343 FORCEINLINE T *Data() 00344 { 00345 return (T*)base::Begin(); 00346 } 00347 00349 FORCEINLINE const T *Data() const 00350 { 00351 return (const T*)base::Begin(); 00352 } 00353 00355 FORCEINLINE T *Data(size_t index) 00356 { 00357 CheckIdx(index); 00358 return (Data() + index); 00359 } 00360 00362 FORCEINLINE const T *Data(size_t index) const 00363 { 00364 CheckIdx(index); 00365 return (Data() + index); 00366 } 00367 00369 FORCEINLINE size_t Size() const 00370 { 00371 return (base::Length() / type_size); 00372 } 00373 00375 FORCEINLINE size_t MaxSize() const 00376 { 00377 return (base::Capacity() / type_size); 00378 } 00379 00381 FORCEINLINE size_t GetReserve() const 00382 { 00383 return ((base::Capacity() - base::Length()) / type_size); 00384 } 00385 00387 FORCEINLINE T *GrowSizeNC(size_t num_items) 00388 { 00389 return (T*)base::Append(num_items * type_size); 00390 } 00391 00396 FORCEINLINE T *MakeFreeSpace(size_t num_items) 00397 { 00398 return (T*)base::Prepare(num_items * type_size); 00399 } 00400 00401 FORCEINLINE OnTransfer Transfer() 00402 { 00403 return OnTransfer(*this); 00404 } 00405 }; 00406 00407 00408 #endif /* BLOB_HPP */