buf_fifo.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright, 2001 Nevrax Ltd.
00006  *
00007  * This file is part of NEVRAX NEL.
00008  * NEVRAX NEL is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2, or (at your option)
00011  * any later version.
00012 
00013  * NEVRAX NEL is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * General Public License for more details.
00017 
00018  * You should have received a copy of the GNU General Public License
00019  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00020  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00021  * MA 02111-1307, USA.
00022  */
00023 
00024 #include "stdmisc.h"
00025 #include "nel/misc/time_nl.h"
00026 #include "nel/misc/command.h"
00027 #include "nel/misc/buf_fifo.h"
00028 
00029 using namespace std;
00030 
00031 #define DEBUG_FIFO 0
00032 
00033 // if 0, don't stat the time of different function
00034 #define STAT_FIFO 1
00035 
00036 namespace NLMISC {
00037 
00038 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
00039 CBufFIFO::TAllBuffers       CBufFIFO::_AllBuffers;
00040 #endif
00041 
00042 
00043 CBufFIFO::CBufFIFO() : _Buffer(NULL), _BufferSize(0), _Empty(true), _Head(NULL), _Tail(NULL), _Rewinder(NULL)
00044 {
00045 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
00046     _AllBuffers.insert(this);
00047 #endif
00048 
00049     // reset statistic
00050     _BiggestBlock = 0;
00051     _SmallestBlock = 999999999;
00052     _BiggestBuffer = 0;
00053     _SmallestBuffer = 999999999;
00054     _Pushed = 0;
00055     _Fronted = 0;
00056     _Resized = 0;
00057     _PushedTime = 0;
00058     _FrontedTime = 0;
00059     _ResizedTime = 0;
00060 }
00061 
00062 CBufFIFO::~CBufFIFO()
00063 {
00064 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
00065     _AllBuffers.erase(this);
00066 #endif
00067 
00068     if (_Buffer != NULL)
00069     {
00070         delete []_Buffer;
00071 #if DEBUG_FIFO
00072         nldebug("%p delete", this);
00073 #endif
00074     }
00075 }
00076 
00077 void     CBufFIFO::push (const uint8 *buffer, uint32 s)
00078 {
00079     // if the buffer is more than 1 meg, there s surely a problem, no?
00080 //  nlassert( buffer.size() < 1000000 ); // size check in debug mode
00081 
00082 #if STAT_FIFO
00083     TTicks before = CTime::getPerformanceTime();
00084 #endif
00085 
00086 #if DEBUG_FIFO
00087     nldebug("%p push(%d)", this, s);
00088 #endif
00089 
00090     nlassert(s > 0 && s < pow(2.0, static_cast<double>(sizeof(TFifoSize)*8)));
00091 
00092     // stat code
00093     if (s > _BiggestBlock) _BiggestBlock = s;
00094     if (s < _SmallestBlock) _SmallestBlock = s;
00095     _Pushed++;
00096 
00097     while (!canFit (s + sizeof (TFifoSize)))
00098     {
00099         resize(_BufferSize * 2);
00100     }
00101 
00102     *(TFifoSize *)_Head = s;
00103     _Head += sizeof(TFifoSize);
00104 
00105     CFastMem::memcpy(_Head, buffer, s);
00106 
00107     _Head += s;
00108 
00109     _Empty = false;
00110 
00111 #if STAT_FIFO
00112     // stat code
00113     TTicks after = CTime::getPerformanceTime();
00114     _PushedTime += after - before;
00115 #endif
00116 
00117 #if DEBUG_FIFO
00118     display ();
00119 #endif
00120 }
00121 
00122 void CBufFIFO::push(const std::vector<uint8> &buffer1, const std::vector<uint8> &buffer2)
00123 {
00124 #if STAT_FIFO
00125     TTicks before = CTime::getPerformanceTime();
00126 #endif
00127 
00128     TFifoSize s = buffer1.size() + buffer2.size();
00129 
00130 #if DEBUG_FIFO
00131     nldebug("%p push2(%d)", this, s);
00132 #endif
00133 
00134     nlassert((buffer1.size() + buffer2.size ()) > 0 && (buffer1.size() + buffer2.size ()) < pow(2.0, static_cast<double>(sizeof(TFifoSize)*8)));
00135 
00136     // avoid too big fifo
00137     if (this->size() > 10000000)
00138     {
00139         throw Exception ("CBufFIFO::push(): stack full (more than 10mb)");
00140     }
00141 
00142 
00143     // stat code
00144     if (s > _BiggestBlock) _BiggestBlock = s;
00145     if (s < _SmallestBlock) _SmallestBlock = s;
00146 
00147     _Pushed++;
00148 
00149     // resize while the buffer is enough big to accept the block
00150     while (!canFit (s + sizeof (TFifoSize)))
00151     {
00152         resize(_BufferSize * 2);
00153     }
00154 
00155     // store the size of the block
00156     *(TFifoSize *)_Head = s;
00157     _Head += sizeof(TFifoSize);
00158 
00159     // store the block itself
00160     CFastMem::memcpy(_Head, &(buffer1[0]), buffer1.size());
00161     CFastMem::memcpy(_Head + buffer1.size(), &(buffer2[0]), buffer2.size());
00162     _Head += s;
00163 
00164     _Empty = false;
00165 
00166 #if STAT_FIFO
00167     // stat code
00168     TTicks after = CTime::getPerformanceTime();
00169     _PushedTime += after - before;
00170 #endif
00171 
00172 #if DEBUG_FIFO
00173     display ();
00174 #endif
00175 }
00176 
00177 void CBufFIFO::pop ()
00178 {
00179     if (empty ())
00180     {
00181         nlwarning("BF: Try to pop an empty fifo!");
00182         return;
00183     }
00184 
00185     if (_Rewinder != NULL && _Tail == _Rewinder)
00186     {
00187 #if DEBUG_FIFO
00188         nldebug("%p pop rewind!", this);
00189 #endif
00190 
00191         // need to rewind
00192         _Tail = _Buffer;
00193         _Rewinder = NULL;
00194     }
00195 
00196     TFifoSize s = *(TFifoSize *)_Tail;
00197 
00198 #if DEBUG_FIFO
00199     nldebug("%p pop(%d)", this, s);
00200 #endif
00201 
00202 #ifdef NL_DEBUG
00203     // clear the message to be sure user doesn't use it anymore
00204     memset (_Tail, '-', s + sizeof (TFifoSize));
00205 #endif
00206 
00207     _Tail += s + sizeof (TFifoSize);
00208 
00209     if (_Tail == _Head) _Empty = true;
00210 
00211 #if DEBUG_FIFO
00212     display ();
00213 #endif
00214 }
00215 
00216 uint8 CBufFIFO::frontLast ()
00217 {
00218     uint8   *tail = _Tail;
00219 
00220     if (empty ())
00221     {
00222         nlwarning("BF: Try to get the front of an empty fifo!");
00223         return 0;
00224     }
00225 
00226     if (_Rewinder != NULL && tail == _Rewinder)
00227     {
00228 #if DEBUG_FIFO
00229         nldebug("%p front rewind!", this);
00230 #endif
00231 
00232         // need to rewind
00233         tail = _Buffer;
00234     }
00235 
00236     TFifoSize s = *(TFifoSize *)tail;
00237 
00238 #if DEBUG_FIFO
00239     nldebug("%p frontLast() returns %d ", this, s, *(tail+sizeof(TFifoSize)+size-1));
00240 #endif
00241 
00242     return *(tail+sizeof(TFifoSize)+s-1);
00243 }
00244 
00245 
00246 void CBufFIFO::front (vector<uint8> &buffer)
00247 {
00248     uint8 *tmpbuffer;
00249     uint32 s;
00250 
00251     buffer.clear ();
00252 
00253     front (tmpbuffer, s);
00254 
00255     buffer.resize (s);
00256 
00257     CFastMem::memcpy (&(buffer[0]), tmpbuffer, s);
00258 
00259 /*  TTicks before = CTime::getPerformanceTime ();
00260 
00261     uint8   *tail = _Tail;
00262 
00263     buffer.clear ();
00264 
00265     if (empty ())
00266     {
00267         nlwarning("Try to get the front of an empty fifo!");
00268         return;
00269     }
00270 
00271     _Fronted++;
00272 
00273     if (_Rewinder != NULL && tail == _Rewinder)
00274     {
00275 #if DEBUG_FIFO
00276         nldebug("%p front rewind!", this);
00277 #endif
00278 
00279         // need to rewind
00280         tail = _Buffer;
00281     }
00282 
00283     TFifoSize size = *(TFifoSize *)tail;
00284 
00285 #if DEBUG_FIFO
00286     nldebug("%p front(%d)", this, size);
00287 #endif
00288 
00289     tail += sizeof (TFifoSize);
00290 
00291     buffer.resize (size);
00292 
00293     CFastMem::memcpy (&(buffer[0]), tail, size);
00294 
00295     // stat code
00296     TTicks after = CTime::getPerformanceTime ();
00297     _FrontedTime += after - before;
00298 
00299 #if DEBUG_FIFO
00300     display ();
00301 #endif
00302 */}
00303 
00304 
00305 void CBufFIFO::front (NLMISC::CMemStream &buffer)
00306 {
00307     uint8 *tmpbuffer;
00308     uint32 s;
00309 
00310     buffer.clear ();
00311 
00312     front (tmpbuffer, s);
00313 
00314     buffer.fill (tmpbuffer, s);
00315 
00316     /*
00317     TTicks before = CTime::getPerformanceTime ();
00318 
00319     uint8   *tail = _Tail;
00320 
00321     buffer.clear ();
00322 
00323     if (empty ())
00324     {
00325         nlwarning("Try to get the front of an empty fifo!");
00326         return;
00327     }
00328 
00329     _Fronted++;
00330 
00331     if (_Rewinder != NULL && tail == _Rewinder)
00332     {
00333 #if DEBUG_FIFO
00334         nldebug("%p front rewind!", this);
00335 #endif
00336 
00337         // need to rewind
00338         tail = _Buffer;
00339     }
00340 
00341     TFifoSize size = *(TFifoSize *)tail;
00342 
00343 #if DEBUG_FIFO
00344     nldebug("%p front(%d)", this, size);
00345 #endif
00346 
00347     tail += sizeof (TFifoSize);
00348 
00349     //buffer.resize (size);
00350     //CFastMem::memcpy (&(buffer[0]), tail, size);
00351 
00352     buffer.fill (tail, size);
00353 
00354     // stat code
00355     TTicks after = CTime::getPerformanceTime ();
00356     _FrontedTime += after - before;
00357 
00358 #if DEBUG_FIFO
00359     display ();
00360 #endif*/
00361 }
00362 
00363 void CBufFIFO::front (uint8 *&buffer, uint32 &s)
00364 {
00365 #if STAT_FIFO
00366     TTicks before = CTime::getPerformanceTime ();
00367 #endif
00368 
00369     uint8   *tail = _Tail;
00370 
00371     if (empty ())
00372     {
00373         nlwarning("BF: Try to get the front of an empty fifo!");
00374         return;
00375     }
00376 
00377     _Fronted++;
00378 
00379     if (_Rewinder != NULL && tail == _Rewinder)
00380     {
00381 #if DEBUG_FIFO
00382         nldebug("%p front rewind!", this);
00383 #endif
00384 
00385         // need to rewind
00386         tail = _Buffer;
00387     }
00388 
00389     s = *(TFifoSize *)tail;
00390 
00391 #if DEBUG_FIFO
00392     nldebug("%p front(%d)", this, s);
00393 #endif
00394 
00395     tail += sizeof (TFifoSize);
00396 
00397 #if STAT_FIFO
00398     // stat code
00399     TTicks after = CTime::getPerformanceTime ();
00400     _FrontedTime += after - before;
00401 #endif
00402 
00403 #if DEBUG_FIFO
00404     display ();
00405 #endif
00406 
00407     buffer = tail;
00408 }
00409 
00410 
00411 
00412 void CBufFIFO::clear ()
00413 {
00414     _Tail = _Head = _Buffer;
00415     _Rewinder = NULL;
00416     _Empty = true;
00417 }
00418 
00419 uint32 CBufFIFO::size ()
00420 {
00421     if (empty ())
00422     {
00423         return 0;
00424     }
00425     else if (_Head == _Tail)
00426     {
00427         // buffer is full
00428         if (_Rewinder == NULL)
00429             return _BufferSize;
00430         else
00431             return _Rewinder - _Buffer;
00432     }
00433     else if (_Head > _Tail)
00434     {
00435         return _Head - _Tail;
00436     }
00437     else if (_Head < _Tail)
00438     {
00439         nlassert (_Rewinder != NULL);
00440         return (_Rewinder - _Tail) + (_Head - _Buffer);
00441     }
00442     nlstop;
00443     return 0;
00444 }
00445 
00446 void CBufFIFO::resize (uint32 s)
00447 {
00448 #if STAT_FIFO
00449     TTicks before = CTime::getPerformanceTime();
00450 #endif
00451 
00452     if (s == 0) s = 100;
00453 
00454 #if DEBUG_FIFO
00455     nldebug("%p resize(%d)", this, s);
00456 #endif
00457 
00458     if (s > _BiggestBuffer) _BiggestBuffer = s;
00459     if (s < _SmallestBuffer) _SmallestBuffer = s;
00460 
00461     _Resized++;
00462 
00463     uint32 UsedSize = CBufFIFO::size();
00464 
00465     // creer un nouveau tableau et copie l ancien dans le nouveau.
00466     if (s < _BufferSize && UsedSize > s)
00467     {
00468         // probleme, on a pas assez de place pour caser les datas => on fait pas
00469         nlwarning("BF: Can't resize the FIFO because there's not enough room in the new wanted buffer (%d bytes needed at least)", UsedSize);
00470         return;
00471     }
00472 
00473     uint8 *NewBuffer = new uint8[s];
00474     if (NewBuffer == NULL)
00475     {
00476         nlerror("Not enough memory to resize the FIFO to %u bytes", s);
00477     }
00478 #ifdef NL_DEBUG
00479     // clear the message to be sure user doesn't use it anymore
00480     memset (NewBuffer, '-', s);
00481 #endif
00482 
00483 #if DEBUG_FIFO
00484     nldebug("%p new %d bytes", this, s);
00485 #endif
00486 
00487     // copy the old buffer to the new one
00488     // if _Tail == _Head => empty fifo, don't copy anything
00489     if (!empty())
00490     {
00491         if (_Tail < _Head)
00492         {
00493             CFastMem::memcpy (NewBuffer, _Tail, UsedSize);
00494         }
00495         else if (_Tail >= _Head)
00496         {
00497             nlassert (_Rewinder != NULL);
00498 
00499             uint size1 = _Rewinder - _Tail;
00500             CFastMem::memcpy (NewBuffer, _Tail, size1);
00501             uint size2 = _Head - _Buffer;
00502             CFastMem::memcpy (NewBuffer + size1, _Buffer, size2);
00503 
00504             nlassert (size1+size2==UsedSize);
00505         }
00506     }
00507 
00508     // resync the circular pointer
00509     // Warning: don't invert these 2 lines position or it ll not work
00510     _Tail = NewBuffer;
00511     _Head = NewBuffer + UsedSize;
00512     _Rewinder = NULL;
00513 
00514     // delete old buffer if needed
00515     if (_Buffer != NULL)
00516     {
00517         delete []_Buffer;
00518 #if DEBUG_FIFO
00519         nldebug ("delete", this);
00520 #endif
00521     }
00522 
00523     // affect new buffer
00524     _Buffer = NewBuffer;
00525     _BufferSize = s;
00526 
00527 #if STAT_FIFO
00528     TTicks after = CTime::getPerformanceTime();
00529     _ResizedTime += after - before;
00530 #endif
00531 
00532 #if DEBUG_FIFO
00533     display ();
00534 #endif
00535 }
00536 
00537 void CBufFIFO::displayStats (CLog *log)
00538 {
00539     log->displayNL ("%p CurrentQueueSize: %d, TotalQueueSize: %d", this, size(), _BufferSize);
00540     log->displayNL ("%p InQueue: %d", this, _Pushed - _Fronted);
00541 
00542     log->displayNL ("%p BiggestBlock: %d, SmallestBlock: %d", this, _BiggestBlock, _SmallestBlock);
00543     log->displayNL ("%p BiggestBuffer: %d, SmallestBuffer: %d", this, _BiggestBuffer, _SmallestBuffer);
00544     log->displayNL ("%p Pushed: %d, PushedTime: total %"NL_I64"d ticks, mean %f ticks", this, _Pushed, _PushedTime, (_Pushed>0?(double)(sint64)_PushedTime / (double)_Pushed:0.0));
00545     log->displayNL ("%p Fronted: %d, FrontedTime: total %"NL_I64"d ticks, mean %f ticks", this, _Fronted, _FrontedTime, (_Fronted>0?(double)(sint64)_FrontedTime / (double)_Fronted:0.0));
00546     log->displayNL ("%p Resized: %d, ResizedTime: total %"NL_I64"d ticks, mean %f ticks", this, _Resized, _ResizedTime, (_Resized>0?(double)(sint64)_ResizedTime / (double)_Resized:0.0));
00547 }
00548 
00549 void CBufFIFO::display ()
00550 {
00551     int s = 64;
00552     int gran = s/30;
00553 
00554     char str[1024];
00555 
00556     smprintf(str, 1024, "%p %p (%5d %5d) %p %p %p ", this, _Buffer, _BufferSize, CBufFIFO::size(), _Rewinder, _Tail, _Head);
00557 
00558     int i;
00559     for (i = 0; i < (sint32) _BufferSize; i+= gran)
00560     {
00561         uint8 *pos = _Buffer + i;
00562         if (_Tail >= pos && _Tail < pos + gran)
00563         {
00564             if (_Head >= pos && _Head < pos + gran)
00565             {
00566                 if (_Rewinder != NULL && _Rewinder >= pos && _Rewinder < pos + gran)
00567                 {
00568                     strncat (str, "*", 1024);
00569                 }
00570                 else
00571                 {
00572                     strncat (str, "@", 1024);
00573                 }
00574             }
00575             else
00576             {
00577                 strncat (str, "T", 1024);
00578             }
00579         }
00580         else if (_Head >= pos && _Head < pos + gran)
00581         {
00582             strncat (str, "H", 1024);
00583         }
00584         else if (_Rewinder != NULL && _Rewinder >= pos && _Rewinder < pos + gran)
00585         {
00586             strncat (str, "R", 1024);
00587         }
00588         else
00589         {
00590             if (strlen(str) < 1023)
00591             {
00592                 uint32 p = strlen(str);
00593                 if (isprint(*pos))
00594                     str[p] = *pos;
00595                 else
00596                     str[p] = '$';
00597 
00598                 str[p+1] = '\0';
00599             }
00600         }
00601     }
00602 
00603     for (; i < s; i+= gran)
00604     {
00605         strncat (str, " ", 1024);
00606     }
00607 #ifdef NL_DEBUG
00608     strncat (str, "\n", 1024);
00609 #else
00610     strncat (str, "\r", 1024);
00611 #endif
00612     DebugLog->display (str);
00613 }
00614 
00615 bool CBufFIFO::canFit (uint32 s)
00616 {
00617     if (_Tail == _Head)
00618     {
00619         if (empty())
00620         {
00621             // is the buffer large enough?
00622             if (_BufferSize >= s)
00623             {
00624                 // reset the pointer
00625 #if DEBUG_FIFO
00626                 nldebug("%p reset tail and head", this);
00627 #endif
00628                 _Head = _Tail = _Buffer;
00629                 return true;
00630             }
00631             else
00632             {
00633                 // buffer not big enough
00634 #if DEBUG_FIFO
00635                 nldebug("%p buffer full buffersize<size", this);
00636 #endif
00637                 return false;
00638             }
00639         }
00640         else
00641         {
00642             // buffer full
00643 #if DEBUG_FIFO
00644             nldebug("%p buffer full h=t", this);
00645 #endif
00646             return false;
00647         }
00648     }
00649     else if (_Tail < _Head)
00650     {
00651         if (_Buffer + _BufferSize - _Head >= (sint32) s)
00652         {
00653             // can fit after _Head
00654 #if DEBUG_FIFO
00655             nldebug("%p fit after", this);
00656 #endif
00657             return true;
00658         }
00659         else if (_Tail - _Buffer >= (sint32) s)
00660         {
00661             // can fit at the beginning
00662 #if DEBUG_FIFO
00663             nldebug("%p fit at beginning", this);
00664 #endif
00665             _Rewinder = _Head;
00666 #if DEBUG_FIFO
00667             nldebug("%p set the rewinder", this);
00668 #endif
00669             _Head = _Buffer;
00670             return true;
00671         }
00672         else
00673         {
00674             // can't fit
00675 #if DEBUG_FIFO
00676             nldebug("%p no room t<h", this);
00677 #endif
00678             return false;
00679         }
00680     }
00681     else // the last case is : if (_Tail > _Head)
00682     {
00683         if (_Tail - _Head >= (sint32) s)
00684         {
00685 #if DEBUG_FIFO
00686             nldebug("%p fit t>h", this);
00687 #endif
00688             return true;
00689         }
00690         else
00691         {
00692 #if DEBUG_FIFO
00693             nldebug("%p no room t>h", this);
00694 #endif
00695             return false;
00696         }
00697     }
00698 }
00699 
00700 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
00701 NLMISC_CATEGORISED_COMMAND(misc, dumpAllBuffers, "Dump all the fifo buffer", "no args")
00702 {
00703     log.displayNL("Dumping %u FIFO buffers :", CBufFIFO::_AllBuffers.size());
00704 
00705     CBufFIFO::TAllBuffers::iterator first(CBufFIFO::_AllBuffers.begin()), last(CBufFIFO::_AllBuffers.end());
00706     for (; first != last; ++first)
00707     {
00708         CBufFIFO *buf = *first;
00709 
00710         log.displayNL("Dumping buffer %p:", buf);
00711 
00712         buf->displayStats(&log);
00713     }
00714 
00715     return true;
00716 }
00717 #endif
00718 
00719 
00720 } // NLMISC

Generated on Thu Jan 7 08:26:33 2010 for NeL by  doxygen 1.6.1