# blob.hpp

Go to the documentation of this file.

    00001 /* $Id: blob.hpp 19134 2010-02-14 18:33:57Z frosch $ */
    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
    00048 class CBlobBaseSimple {
    00049 public:
    00050   typedef ::ptrdiff_t bsize_t;
    00051   typedef ::byte      bitem_t;
    00052
    00053 protected:
    00055   struct CHdr {
    00056     bsize_t    m_size;
    00057     bsize_t    m_max_size;
    00058   };
    00059
    00061   union {
    00062     bitem_t    *m_pData;
    00063     CHdr       *m_pHdr_1;
    00064   } ptr_u;
    00065
    00066 private:
    00072   static CHdr hdrEmpty[];
    00073
    00074 public:
    00075   static const bsize_t Ttail_reserve = 4;
    00076
    00078   FORCEINLINE CBlobBaseSimple() { InitEmpty(); }
    00080   FORCEINLINE CBlobBaseSimple(const bitem_t *p, bsize_t num_bytes)
    00081   {
    00082     InitEmpty();
    00083     AppendRaw(p, num_bytes);
    00084   }
    00085
    00087   FORCEINLINE CBlobBaseSimple(const CBlobBaseSimple& src)
    00088   {
    00089     InitEmpty();
    00090     AppendRaw(src);
    00091   }
    00092
    00094   FORCEINLINE CBlobBaseSimple(CHdr * const & pHdr_1)
    00095   {
    00096     assert(pHdr_1 != NULL);
    00097     ptr_u.m_pHdr_1 = pHdr_1;
    00098     *const_cast<CHdr**>(&pHdr_1) = NULL;
    00099   }
    00100
    00102   FORCEINLINE ~CBlobBaseSimple()
    00103   {
    00104     Free();
    00105   }
    00106
    00107 protected:
    00110   FORCEINLINE void InitEmpty()
    00111   {
    00112     ptr_u.m_pHdr_1 = const_cast<CHdr *>(&CBlobBaseSimple::hdrEmpty[1]);
    00113   }
    00114
    00116   FORCEINLINE void Init(CHdr *hdr)
    00117   {
    00118     ptr_u.m_pHdr_1 = &hdr[1];
    00119   }
    00120
    00122   FORCEINLINE CHdr& Hdr()
    00123   {
    00124     return *(ptr_u.m_pHdr_1 - 1);
    00125   }
    00126
    00128   FORCEINLINE const CHdr& Hdr() const
    00129   {
    00130     return *(ptr_u.m_pHdr_1 - 1);
    00131   }
    00132
    00134   FORCEINLINE bsize_t& RawSizeRef()
    00135   {
    00136     return Hdr().m_size;
    00137   };
    00138
    00139 public:
    00141   FORCEINLINE bool IsEmpty() const
    00142   {
    00143     return RawSize() == 0;
    00144   }
    00145
    00147   FORCEINLINE bsize_t RawSize() const
    00148   {
    00149     return Hdr().m_size;
    00150   };
    00151
    00153   FORCEINLINE bsize_t MaxRawSize() const
    00154   {
    00155     return Hdr().m_max_size;
    00156   };
    00157
    00159   FORCEINLINE bitem_t *RawData()
    00160   {
    00161     return ptr_u.m_pData;
    00162   }
    00163
    00165   FORCEINLINE const bitem_t *RawData() const
    00166   {
    00167     return ptr_u.m_pData;
    00168   }
    00169
    00171   //FORCEINLINE bsize_t Crc32() const
    00172   //{
    00173   //  return CCrc32::Calc(RawData(), RawSize());
    00174   //}
    00175
    00177   FORCEINLINE void Clear()
    00178   {
    00179     RawSizeRef() = 0;
    00180   }
    00181
    00183   FORCEINLINE void Free()
    00184   {
    00185     if (MaxRawSize() > 0) {
    00186       RawFree(&Hdr());
    00187       InitEmpty();
    00188     }
    00189   }
    00190
    00192   FORCEINLINE void CopyFrom(const CBlobBaseSimple& src)
    00193   {
    00194     Clear();
    00195     AppendRaw(src);
    00196   }
    00197
    00199   FORCEINLINE void MoveFrom(CBlobBaseSimple& src)
    00200   {
    00201     Free();
    00202     ptr_u.m_pData = src.ptr_u.m_pData;
    00203     src.InitEmpty();
    00204   }
    00205
    00207   FORCEINLINE void Swap(CBlobBaseSimple& src)
    00208   {
    00209     bitem_t *tmp = ptr_u.m_pData; ptr_u.m_pData = src.ptr_u.m_pData;
    00210     src.ptr_u.m_pData = tmp;
    00211   }
    00212
    00214   FORCEINLINE void AppendRaw(const void *p, bsize_t num_bytes)
    00215   {
    00216     assert(p != NULL);
    00217     if (num_bytes > 0) {
    00218       memcpy(GrowRawSize(num_bytes), p, num_bytes);
    00219     } else {
    00220       assert(num_bytes >= 0);
    00221     }
    00222   }
    00223
    00225   FORCEINLINE void AppendRaw(const CBlobBaseSimple& src)
    00226   {
    00227     if (!src.IsEmpty())
    00228       memcpy(GrowRawSize(src.RawSize()), src.RawData(), src.RawSize());
    00229   }
    00230
    00233   FORCEINLINE bitem_t *MakeRawFreeSpace(bsize_t num_bytes)
    00234   {
    00235     assert(num_bytes >= 0);
    00236     bsize_t new_size = RawSize() + num_bytes;
    00237     if (new_size > MaxRawSize()) SmartAlloc(new_size);
    00238     return ptr_u.m_pData + RawSize();
    00239   }
    00240
    00243   FORCEINLINE bitem_t *GrowRawSize(bsize_t num_bytes)
    00244   {
    00245     bitem_t *pNewData = MakeRawFreeSpace(num_bytes);
    00246     RawSizeRef() += num_bytes;
    00247     return pNewData;
    00248   }
    00249
    00251   FORCEINLINE void ReduceRawSize(bsize_t num_bytes)
    00252   {
    00253     if (MaxRawSize() > 0 && num_bytes > 0) {
    00254       assert(num_bytes <= RawSize());
    00255       if (num_bytes < RawSize()) {
    00256         RawSizeRef() -= num_bytes;
    00257       } else {
    00258         RawSizeRef() = 0;
    00259       }
    00260     }
    00261   }
    00262
    00264   void SmartAlloc(bsize_t new_size)
    00265   {
    00266     bsize_t old_max_size = MaxRawSize();
    00267     if (old_max_size >= new_size) return;
    00268     /* calculate minimum block size we need to allocate */
    00269     bsize_t min_alloc_size = sizeof(CHdr) + new_size + Ttail_reserve;
    00270     /* ask allocation policy for some reasonable block size */
    00271     bsize_t alloc_size = AllocPolicy(min_alloc_size);
    00272     /* allocate new block */
    00273     CHdr *pNewHdr = RawAlloc(alloc_size);
    00274     /* setup header */
    00275     pNewHdr->m_size = RawSize();
    00276     pNewHdr->m_max_size = alloc_size - (sizeof(CHdr) + Ttail_reserve);
    00277     /* copy existing data */
    00278     if (RawSize() > 0)
    00279       memcpy(pNewHdr + 1, ptr_u.m_pData, pNewHdr->m_size);
    00280     /* replace our block with new one */
    00281     CHdr *pOldHdr = &Hdr();
    00282     Init(pNewHdr);
    00283     if (old_max_size > 0)
    00284       RawFree(pOldHdr);
    00285   }
    00286
    00288   FORCEINLINE static bsize_t AllocPolicy(bsize_t min_alloc)
    00289   {
    00290     if (min_alloc < (1 << 9)) {
    00291       if (min_alloc < (1 << 5)) return (1 << 5);
    00292       return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9);
    00293     }
    00294     if (min_alloc < (1 << 15)) {
    00295       if (min_alloc < (1 << 11)) return (1 << 11);
    00296       return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15);
    00297     }
    00298     if (min_alloc < (1 << 20)) {
    00299       if (min_alloc < (1 << 17)) return (1 << 17);
    00300       return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20);
    00301     }
    00302     min_alloc = (min_alloc | ((1 << 20) - 1)) + 1;
    00303     return min_alloc;
    00304   }
    00305
    00307   static FORCEINLINE CHdr *RawAlloc(bsize_t num_bytes)
    00308   {
    00309     return (CHdr*)MallocT<byte>(num_bytes);
    00310   }
    00311
    00313   static FORCEINLINE void RawFree(CHdr *p)
    00314   {
    00315     /* Just to silence an unsilencable GCC 4.4+ warning. */
    00316     assert(p != CBlobBaseSimple::hdrEmpty);
    00317
    00318     /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */
    00319     free(p);
    00320   }
    00322   FORCEINLINE void FixTail() const
    00323   {
    00324     if (MaxRawSize() > 0) {
    00325       bitem_t *p = &ptr_u.m_pData[RawSize()];
    00326       for (bsize_t i = 0; i < Ttail_reserve; i++) {
    00327         p[i] = 0;
    00328       }
    00329     }
    00330   }
    00331 };
    00332
    00340 template <class Titem_, class Tbase_ = CBlobBaseSimple>
    00341 class CBlobT : public Tbase_ {
    00342   /* make template arguments public: */
    00343 public:
    00344   typedef Titem_ Titem;
    00345   typedef Tbase_ Tbase;
    00346   typedef typename Tbase::bsize_t bsize_t;
    00347
    00348   static const bsize_t Titem_size = sizeof(Titem);
    00349
    00350   struct OnTransfer {
    00351     typename Tbase_::CHdr *m_pHdr_1;
    00352     OnTransfer(const OnTransfer& src) : m_pHdr_1(src.m_pHdr_1) {assert(src.m_pHdr_1 != NULL); *const_cast<typename Tbase_::CHdr**>(&src.m_pHdr_1) = NULL;}
    00353     OnTransfer(CBlobT& src) : m_pHdr_1(src.ptr_u.m_pHdr_1) {src.InitEmpty();}
    00354     ~OnTransfer() {assert(m_pHdr_1 == NULL);}
    00355   };
    00356
    00358   FORCEINLINE CBlobT()
    00359     : Tbase()
    00360   {}
    00361
    00363   FORCEINLINE CBlobT(const Titem_ *p, bsize_t num_items)
    00364     : Tbase((typename Tbase_::bitem_t*)p, num_items * Titem_size)
    00365   {}
    00366
    00368   FORCEINLINE CBlobT(const Tbase& src)
    00369     : Tbase(src)
    00370   {
    00371     assert((Tbase::RawSize() % Titem_size) == 0);
    00372   }
    00373
    00375   FORCEINLINE CBlobT(const OnTransfer& ot)
    00376     : Tbase(ot.m_pHdr_1)
    00377   {}
    00378
    00380   FORCEINLINE ~CBlobT()
    00381   {
    00382     Free();
    00383   }
    00384
    00386   FORCEINLINE void CheckIdx(bsize_t idx) const
    00387   {
    00388     assert(idx >= 0); assert(idx < Size());
    00389   }
    00390
    00392   FORCEINLINE Titem *Data()
    00393   {
    00394     return (Titem*)Tbase::RawData();
    00395   }
    00396
    00398   FORCEINLINE const Titem *Data() const
    00399   {
    00400     return (const Titem*)Tbase::RawData();
    00401   }
    00402
    00404   FORCEINLINE Titem *Data(bsize_t idx)
    00405   {
    00406     CheckIdx(idx);
    00407     return (Data() + idx);
    00408   }
    00409
    00411   FORCEINLINE const Titem *Data(bsize_t idx) const
    00412   {
    00413     CheckIdx(idx);
    00414     return (Data() + idx);
    00415   }
    00416
    00418   FORCEINLINE bsize_t Size() const
    00419   {
    00420     return (Tbase::RawSize() / Titem_size);
    00421   }
    00422
    00424   FORCEINLINE bsize_t MaxSize() const
    00425   {
    00426     return (Tbase::MaxRawSize() / Titem_size);
    00427   }
    00429   FORCEINLINE bsize_t GetReserve() const
    00430   {
    00431     return ((Tbase::MaxRawSize() - Tbase::RawSize()) / Titem_size);
    00432   }
    00433
    00435   FORCEINLINE void Free()
    00436   {
    00437     assert((Tbase::RawSize() % Titem_size) == 0);
    00438     bsize_t old_size = Size();
    00439     if (old_size > 0) {
    00440       /* destroy removed items; */
    00441       Titem *pI_last_to_destroy = Data(0);
    00442       for (Titem *pI = Data(old_size - 1); pI >= pI_last_to_destroy; pI--) pI->~Titem_();
    00443     }
    00444     Tbase::Free();
    00445   }
    00446
    00448   FORCEINLINE Titem *GrowSizeNC(bsize_t num_items)
    00449   {
    00450     return (Titem*)Tbase::GrowRawSize(num_items * Titem_size);
    00451   }
    00452
    00454   FORCEINLINE Titem *GrowSizeC(bsize_t num_items)
    00455   {
    00456     Titem *pI = GrowSizeNC(num_items);
    00457     for (bsize_t i = num_items; i > 0; i--, pI++) new (pI) Titem();
    00458   }
    00459
    00461   FORCEINLINE void ReduceSize(bsize_t num_items)
    00462   {
    00463     assert((Tbase::RawSize() % Titem_size) == 0);
    00464     bsize_t old_size = Size();
    00465     assert(num_items <= old_size);
    00466     bsize_t new_size = (num_items <= old_size) ? (old_size - num_items) : 0;
    00467     /* destroy removed items; */
    00468     Titem *pI_last_to_destroy = Data(new_size);
    00469     for (Titem *pI = Data(old_size - 1); pI >= pI_last_to_destroy; pI--) pI->~Titem();
    00470     /* remove them */
    00471     Tbase::ReduceRawSize(num_items * Titem_size);
    00472   }
    00473
    00475   FORCEINLINE Titem *AppendNew()
    00476   {
    00477     Titem& dst = *GrowSizeNC(1); // Grow size by one item
    00478     Titem *pNewItem = new (&dst) Titem(); // construct the new item by calling in-place new operator
    00479     return pNewItem;
    00480   }
    00481
    00483   FORCEINLINE Titem *Append(const Titem& src)
    00484   {
    00485     Titem& dst = *GrowSizeNC(1); // Grow size by one item
    00486     Titem *pNewItem = new (&dst) Titem(src); // construct the new item by calling in-place new operator with copy ctor()
    00487     return pNewItem;
    00488   }
    00489
    00491   FORCEINLINE Titem *Append(const Titem *pSrc, bsize_t num_items)
    00492   {
    00493     Titem *pDst = GrowSizeNC(num_items);
    00494     Titem *pDstOrg = pDst;
    00495     Titem *pDstEnd = pDst + num_items;
    00496     while (pDst < pDstEnd) new (pDst++) Titem(*(pSrc++));
    00497     return pDstOrg;
    00498   }
    00499
    00501   FORCEINLINE void RemoveBySwap(bsize_t idx)
    00502   {
    00503     CheckIdx(idx);
    00504     /* destroy removed item */
    00505     Titem *pRemoved = Data(idx);
    00506     RemoveBySwap(pRemoved);
    00507   }
    00508
    00510   FORCEINLINE void RemoveBySwap(Titem *pItem)
    00511   {
    00512     Titem *pLast = Data(Size() - 1);
    00513     assert(pItem >= Data() && pItem <= pLast);
    00514     /* move last item to its new place */
    00515     if (pItem != pLast) {
    00516       pItem->~Titem_();
    00517       new (pItem) Titem_(*pLast);
    00518     }
    00519     /* destroy the last item */
    00520     pLast->~Titem_();
    00521     /* and reduce the raw blob size */
    00522     Tbase::ReduceRawSize(Titem_size);
    00523   }
    00524
    00527   FORCEINLINE Titem *MakeFreeSpace(bsize_t num_items)
    00528   {
    00529     return (Titem*)Tbase::MakeRawFreeSpace(num_items * Titem_size);
    00530   }
    00531
    00532   FORCEINLINE OnTransfer Transfer()
    00533   {
    00534     return OnTransfer(*this);
    00535   };
    00536 };
    00537
    00538
    00539 #endif /* BLOB_HPP */