# blob.hpp

Go to the documentation of this file.

    00001 /* $Id: blob.hpp 18809 2010-01-15 16:41:15Z rubidium $ */
    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:
    00068   static const CHdr hdrEmpty[];
    00069
    00070 public:
    00071   static const bsize_t Ttail_reserve = 4;
    00072
    00074   FORCEINLINE CBlobBaseSimple() { InitEmpty(); }
    00076   FORCEINLINE CBlobBaseSimple(const bitem_t *p, bsize_t num_bytes)
    00077   {
    00078     InitEmpty();
    00079     AppendRaw(p, num_bytes);
    00080   }
    00081
    00083   FORCEINLINE CBlobBaseSimple(const CBlobBaseSimple& src)
    00084   {
    00085     InitEmpty();
    00086     AppendRaw(src);
    00087   }
    00088
    00090   FORCEINLINE CBlobBaseSimple(CHdr * const & pHdr_1)
    00091   {
    00092     assert(pHdr_1 != NULL);
    00093     ptr_u.m_pHdr_1 = pHdr_1;
    00094     *const_cast<CHdr**>(&pHdr_1) = NULL;
    00095   }
    00096
    00098   FORCEINLINE ~CBlobBaseSimple()
    00099   {
    00100     Free();
    00101   }
    00102
    00103 protected:
    00106   FORCEINLINE void InitEmpty()
    00107   {
    00108     ptr_u.m_pHdr_1 = const_cast<CHdr *>(&CBlobBaseSimple::hdrEmpty[1]);
    00109   }
    00110
    00112   FORCEINLINE void Init(CHdr *hdr)
    00113   {
    00114     ptr_u.m_pHdr_1 = &hdr[1];
    00115   }
    00116
    00118   FORCEINLINE CHdr& Hdr()
    00119   {
    00120     return ptr_u.m_pHdr_1[-1];
    00121   }
    00122
    00124   FORCEINLINE const CHdr& Hdr() const
    00125   {
    00126     return ptr_u.m_pHdr_1[-1];
    00127   }
    00128
    00130   FORCEINLINE bsize_t& RawSizeRef()
    00131   {
    00132     return Hdr().m_size;
    00133   };
    00134
    00135 public:
    00137   FORCEINLINE bool IsEmpty() const
    00138   {
    00139     return RawSize() == 0;
    00140   }
    00141
    00143   FORCEINLINE bsize_t RawSize() const
    00144   {
    00145     return Hdr().m_size;
    00146   };
    00147
    00149   FORCEINLINE bsize_t MaxRawSize() const
    00150   {
    00151     return Hdr().m_max_size;
    00152   };
    00153
    00155   FORCEINLINE bitem_t *RawData()
    00156   {
    00157     return ptr_u.m_pData;
    00158   }
    00159
    00161   FORCEINLINE const bitem_t *RawData() const
    00162   {
    00163     return ptr_u.m_pData;
    00164   }
    00165
    00167   //FORCEINLINE bsize_t Crc32() const
    00168   //{
    00169   //  return CCrc32::Calc(RawData(), RawSize());
    00170   //}
    00171
    00173   FORCEINLINE void Clear()
    00174   {
    00175     RawSizeRef() = 0;
    00176   }
    00177
    00179   FORCEINLINE void Free()
    00180   {
    00181     if (MaxRawSize() > 0) {
    00182       RawFree(&Hdr());
    00183       InitEmpty();
    00184     }
    00185   }
    00186
    00188   FORCEINLINE void CopyFrom(const CBlobBaseSimple& src)
    00189   {
    00190     Clear();
    00191     AppendRaw(src);
    00192   }
    00193
    00195   FORCEINLINE void MoveFrom(CBlobBaseSimple& src)
    00196   {
    00197     Free();
    00198     ptr_u.m_pData = src.ptr_u.m_pData;
    00199     src.InitEmpty();
    00200   }
    00201
    00203   FORCEINLINE void Swap(CBlobBaseSimple& src)
    00204   {
    00205     bitem_t *tmp = ptr_u.m_pData; ptr_u.m_pData = src.ptr_u.m_pData;
    00206     src.ptr_u.m_pData = tmp;
    00207   }
    00208
    00210   FORCEINLINE void AppendRaw(const void *p, bsize_t num_bytes)
    00211   {
    00212     assert(p != NULL);
    00213     if (num_bytes > 0) {
    00214       memcpy(GrowRawSize(num_bytes), p, num_bytes);
    00215     } else {
    00216       assert(num_bytes >= 0);
    00217     }
    00218   }
    00219
    00221   FORCEINLINE void AppendRaw(const CBlobBaseSimple& src)
    00222   {
    00223     if (!src.IsEmpty())
    00224       memcpy(GrowRawSize(src.RawSize()), src.RawData(), src.RawSize());
    00225   }
    00226
    00229   FORCEINLINE bitem_t *MakeRawFreeSpace(bsize_t num_bytes)
    00230   {
    00231     assert(num_bytes >= 0);
    00232     bsize_t new_size = RawSize() + num_bytes;
    00233     if (new_size > MaxRawSize()) SmartAlloc(new_size);
    00234     return ptr_u.m_pData + RawSize();
    00235   }
    00236
    00239   FORCEINLINE bitem_t *GrowRawSize(bsize_t num_bytes)
    00240   {
    00241     bitem_t *pNewData = MakeRawFreeSpace(num_bytes);
    00242     RawSizeRef() += num_bytes;
    00243     return pNewData;
    00244   }
    00245
    00247   FORCEINLINE void ReduceRawSize(bsize_t num_bytes)
    00248   {
    00249     if (MaxRawSize() > 0 && num_bytes > 0) {
    00250       assert(num_bytes <= RawSize());
    00251       if (num_bytes < RawSize()) {
    00252         RawSizeRef() -= num_bytes;
    00253       } else {
    00254         RawSizeRef() = 0;
    00255       }
    00256     }
    00257   }
    00258
    00260   void SmartAlloc(bsize_t new_size)
    00261   {
    00262     bsize_t old_max_size = MaxRawSize();
    00263     if (old_max_size >= new_size) return;
    00264     /* calculate minimum block size we need to allocate */
    00265     bsize_t min_alloc_size = sizeof(CHdr) + new_size + Ttail_reserve;
    00266     /* ask allocation policy for some reasonable block size */
    00267     bsize_t alloc_size = AllocPolicy(min_alloc_size);
    00268     /* allocate new block */
    00269     CHdr *pNewHdr = RawAlloc(alloc_size);
    00270     /* setup header */
    00271     pNewHdr->m_size = RawSize();
    00272     pNewHdr->m_max_size = alloc_size - (sizeof(CHdr) + Ttail_reserve);
    00273     /* copy existing data */
    00274     if (RawSize() > 0)
    00275       memcpy(pNewHdr + 1, ptr_u.m_pData, pNewHdr->m_size);
    00276     /* replace our block with new one */
    00277     CHdr *pOldHdr = &Hdr();
    00278     Init(pNewHdr);
    00279     if (old_max_size > 0)
    00280       RawFree(pOldHdr);
    00281   }
    00282
    00284   FORCEINLINE static bsize_t AllocPolicy(bsize_t min_alloc)
    00285   {
    00286     if (min_alloc < (1 << 9)) {
    00287       if (min_alloc < (1 << 5)) return (1 << 5);
    00288       return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9);
    00289     }
    00290     if (min_alloc < (1 << 15)) {
    00291       if (min_alloc < (1 << 11)) return (1 << 11);
    00292       return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15);
    00293     }
    00294     if (min_alloc < (1 << 20)) {
    00295       if (min_alloc < (1 << 17)) return (1 << 17);
    00296       return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20);
    00297     }
    00298     min_alloc = (min_alloc | ((1 << 20) - 1)) + 1;
    00299     return min_alloc;
    00300   }
    00301
    00303   static FORCEINLINE CHdr *RawAlloc(bsize_t num_bytes)
    00304   {
    00305     return (CHdr*)MallocT<byte>(num_bytes);
    00306   }
    00307
    00309   static FORCEINLINE void RawFree(CHdr *p)
    00310   {
    00311     /* Just to silence an unsilencable GCC 4.4+ warning. */
    00312     assert(p != CBlobBaseSimple::hdrEmpty);
    00313
    00314     /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */
    00315     free(p);
    00316   }
    00318   FORCEINLINE void FixTail() const
    00319   {
    00320     if (MaxRawSize() > 0) {
    00321       bitem_t *p = &ptr_u.m_pData[RawSize()];
    00322       for (bsize_t i = 0; i < Ttail_reserve; i++) {
    00323         p[i] = 0;
    00324       }
    00325     }
    00326   }
    00327 };
    00328
    00336 template <class Titem_, class Tbase_ = CBlobBaseSimple>
    00337 class CBlobT : public Tbase_ {
    00338   /* make template arguments public: */
    00339 public:
    00340   typedef Titem_ Titem;
    00341   typedef Tbase_ Tbase;
    00342   typedef typename Tbase::bsize_t bsize_t;
    00343
    00344   static const bsize_t Titem_size = sizeof(Titem);
    00345
    00346   struct OnTransfer {
    00347     typename Tbase_::CHdr *m_pHdr_1;
    00348     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;}
    00349     OnTransfer(CBlobT& src) : m_pHdr_1(src.ptr_u.m_pHdr_1) {src.InitEmpty();}
    00350     ~OnTransfer() {assert(m_pHdr_1 == NULL);}
    00351   };
    00352
    00354   FORCEINLINE CBlobT()
    00355     : Tbase()
    00356   {}
    00357
    00359   FORCEINLINE CBlobT(const Titem_ *p, bsize_t num_items)
    00360     : Tbase((typename Tbase_::bitem_t*)p, num_items * Titem_size)
    00361   {}
    00362
    00364   FORCEINLINE CBlobT(const Tbase& src)
    00365     : Tbase(src)
    00366   {
    00367     assert((Tbase::RawSize() % Titem_size) == 0);
    00368   }
    00369
    00371   FORCEINLINE CBlobT(const OnTransfer& ot)
    00372     : Tbase(ot.m_pHdr_1)
    00373   {}
    00374
    00376   FORCEINLINE ~CBlobT()
    00377   {
    00378     Free();
    00379   }
    00380
    00382   FORCEINLINE void CheckIdx(bsize_t idx) const
    00383   {
    00384     assert(idx >= 0); assert(idx < Size());
    00385   }
    00386
    00388   FORCEINLINE Titem *Data()
    00389   {
    00390     return (Titem*)Tbase::RawData();
    00391   }
    00392
    00394   FORCEINLINE const Titem *Data() const
    00395   {
    00396     return (const Titem*)Tbase::RawData();
    00397   }
    00398
    00400   FORCEINLINE Titem *Data(bsize_t idx)
    00401   {
    00402     CheckIdx(idx);
    00403     return (Data() + idx);
    00404   }
    00405
    00407   FORCEINLINE const Titem *Data(bsize_t idx) const
    00408   {
    00409     CheckIdx(idx);
    00410     return (Data() + idx);
    00411   }
    00412
    00414   FORCEINLINE bsize_t Size() const
    00415   {
    00416     return (Tbase::RawSize() / Titem_size);
    00417   }
    00418
    00420   FORCEINLINE bsize_t MaxSize() const
    00421   {
    00422     return (Tbase::MaxRawSize() / Titem_size);
    00423   }
    00425   FORCEINLINE bsize_t GetReserve() const
    00426   {
    00427     return ((Tbase::MaxRawSize() - Tbase::RawSize()) / Titem_size);
    00428   }
    00429
    00431   FORCEINLINE void Free()
    00432   {
    00433     assert((Tbase::RawSize() % Titem_size) == 0);
    00434     bsize_t old_size = Size();
    00435     if (old_size > 0) {
    00436       /* destroy removed items; */
    00437       Titem *pI_last_to_destroy = Data(0);
    00438       for (Titem *pI = Data(old_size - 1); pI >= pI_last_to_destroy; pI--) pI->~Titem_();
    00439     }
    00440     Tbase::Free();
    00441   }
    00442
    00444   FORCEINLINE Titem *GrowSizeNC(bsize_t num_items)
    00445   {
    00446     return (Titem*)Tbase::GrowRawSize(num_items * Titem_size);
    00447   }
    00448
    00450   FORCEINLINE Titem *GrowSizeC(bsize_t num_items)
    00451   {
    00452     Titem *pI = GrowSizeNC(num_items);
    00453     for (bsize_t i = num_items; i > 0; i--, pI++) new (pI) Titem();
    00454   }
    00455
    00457   FORCEINLINE void ReduceSize(bsize_t num_items)
    00458   {
    00459     assert((Tbase::RawSize() % Titem_size) == 0);
    00460     bsize_t old_size = Size();
    00461     assert(num_items <= old_size);
    00462     bsize_t new_size = (num_items <= old_size) ? (old_size - num_items) : 0;
    00463     /* destroy removed items; */
    00464     Titem *pI_last_to_destroy = Data(new_size);
    00465     for (Titem *pI = Data(old_size - 1); pI >= pI_last_to_destroy; pI--) pI->~Titem();
    00466     /* remove them */
    00467     Tbase::ReduceRawSize(num_items * Titem_size);
    00468   }
    00469
    00471   FORCEINLINE Titem *AppendNew()
    00472   {
    00473     Titem& dst = *GrowSizeNC(1); // Grow size by one item
    00474     Titem *pNewItem = new (&dst) Titem(); // construct the new item by calling in-place new operator
    00475     return pNewItem;
    00476   }
    00477
    00479   FORCEINLINE Titem *Append(const Titem& src)
    00480   {
    00481     Titem& dst = *GrowSizeNC(1); // Grow size by one item
    00482     Titem *pNewItem = new (&dst) Titem(src); // construct the new item by calling in-place new operator with copy ctor()
    00483     return pNewItem;
    00484   }
    00485
    00487   FORCEINLINE Titem *Append(const Titem *pSrc, bsize_t num_items)
    00488   {
    00489     Titem *pDst = GrowSizeNC(num_items);
    00490     Titem *pDstOrg = pDst;
    00491     Titem *pDstEnd = pDst + num_items;
    00492     while (pDst < pDstEnd) new (pDst++) Titem(*(pSrc++));
    00493     return pDstOrg;
    00494   }
    00495
    00497   FORCEINLINE void RemoveBySwap(bsize_t idx)
    00498   {
    00499     CheckIdx(idx);
    00500     /* destroy removed item */
    00501     Titem *pRemoved = Data(idx);
    00502     RemoveBySwap(pRemoved);
    00503   }
    00504
    00506   FORCEINLINE void RemoveBySwap(Titem *pItem)
    00507   {
    00508     Titem *pLast = Data(Size() - 1);
    00509     assert(pItem >= Data() && pItem <= pLast);
    00510     /* move last item to its new place */
    00511     if (pItem != pLast) {
    00512       pItem->~Titem_();
    00513       new (pItem) Titem_(*pLast);
    00514     }
    00515     /* destroy the last item */
    00516     pLast->~Titem_();
    00517     /* and reduce the raw blob size */
    00518     Tbase::ReduceRawSize(Titem_size);
    00519   }
    00520
    00523   FORCEINLINE Titem *MakeFreeSpace(bsize_t num_items)
    00524   {
    00525     return (Titem*)Tbase::MakeRawFreeSpace(num_items * Titem_size);
    00526   }
    00527
    00528   FORCEINLINE OnTransfer Transfer()
    00529   {
    00530     return OnTransfer(*this);
    00531   };
    00532 };
    00533
    00534
    00535 #endif /* BLOB_HPP */