// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdmisc.h" #include "nel/misc/time_nl.h" #include "nel/misc/command.h" #include "nel/misc/buf_fifo.h" using namespace std; #define DEBUG_FIFO 0 // if 0, don't stat the time of different function #define STAT_FIFO 1 #ifdef DEBUG_NEW #define new DEBUG_NEW #endif namespace NLMISC { #ifdef BUFFIFO_TRACK_ALL_BUFFERS CBufFIFO::TAllBuffers CBufFIFO::_AllBuffers; #endif CBufFIFO::CBufFIFO() : _Buffer(NULL), _BufferSize(0), _Empty(true), _Head(NULL), _Tail(NULL), _Rewinder(NULL) { #ifdef BUFFIFO_TRACK_ALL_BUFFERS _AllBuffers.insert(this); #endif // reset statistic _BiggestBlock = 0; _SmallestBlock = 999999999; _BiggestBuffer = 0; _SmallestBuffer = 999999999; _Pushed = 0; _Fronted = 0; _Resized = 0; _PushedTime = 0; _FrontedTime = 0; _ResizedTime = 0; } CBufFIFO::~CBufFIFO() { #ifdef BUFFIFO_TRACK_ALL_BUFFERS _AllBuffers.erase(this); #endif if (_Buffer != NULL) { delete []_Buffer; #if DEBUG_FIFO nldebug("%p delete", this); #endif } } void CBufFIFO::push (const uint8 *buffer, uint32 s) { // if the buffer is more than 1 meg, there s surely a problem, no? // nlassert( buffer.size() < 1000000 ); // size check in debug mode #if STAT_FIFO TTicks before = CTime::getPerformanceTime(); #endif #if DEBUG_FIFO nldebug("%p push(%d)", this, s); #endif nlassert(s > 0 && s < pow(2.0, static_cast(sizeof(TFifoSize)*8))); // stat code if (s > _BiggestBlock) _BiggestBlock = s; if (s < _SmallestBlock) _SmallestBlock = s; _Pushed++; while (!canFit (s + sizeof (TFifoSize))) { resize(_BufferSize * 2); } *(TFifoSize *)_Head = s; _Head += sizeof(TFifoSize); CFastMem::memcpy(_Head, buffer, s); _Head += s; _Empty = false; #if STAT_FIFO // stat code TTicks after = CTime::getPerformanceTime(); _PushedTime += after - before; #endif #if DEBUG_FIFO display (); #endif } void CBufFIFO::push(const std::vector &buffer1, const std::vector &buffer2) { #if STAT_FIFO TTicks before = CTime::getPerformanceTime(); #endif TFifoSize s = (TFifoSize)(buffer1.size() + buffer2.size()); #if DEBUG_FIFO nldebug("%p push2(%d)", this, s); #endif nlassert((buffer1.size() + buffer2.size ()) > 0 && (buffer1.size() + buffer2.size ()) < pow(2.0, static_cast(sizeof(TFifoSize)*8))); // avoid too big fifo if (this->size() > 10000000) { throw Exception ("CBufFIFO::push(): stack full (more than 10mb)"); } // stat code if (s > _BiggestBlock) _BiggestBlock = s; if (s < _SmallestBlock) _SmallestBlock = s; _Pushed++; // resize while the buffer is enough big to accept the block while (!canFit (s + sizeof (TFifoSize))) { resize(_BufferSize * 2); } // store the size of the block *(TFifoSize *)_Head = s; _Head += sizeof(TFifoSize); // store the block itself CFastMem::memcpy(_Head, &(buffer1[0]), buffer1.size()); CFastMem::memcpy(_Head + buffer1.size(), &(buffer2[0]), buffer2.size()); _Head += s; _Empty = false; #if STAT_FIFO // stat code TTicks after = CTime::getPerformanceTime(); _PushedTime += after - before; #endif #if DEBUG_FIFO display (); #endif } void CBufFIFO::pop () { if (empty ()) { nlwarning("BF: Try to pop an empty fifo!"); return; } if (_Rewinder != NULL && _Tail == _Rewinder) { #if DEBUG_FIFO nldebug("%p pop rewind!", this); #endif // need to rewind _Tail = _Buffer; _Rewinder = NULL; } TFifoSize s = *(TFifoSize *)_Tail; #if DEBUG_FIFO nldebug("%p pop(%d)", this, s); #endif #ifdef NL_DEBUG // clear the message to be sure user doesn't use it anymore memset (_Tail, '-', s + sizeof (TFifoSize)); #endif _Tail += s + sizeof (TFifoSize); if (_Tail == _Head) _Empty = true; #if DEBUG_FIFO display (); #endif } uint8 CBufFIFO::frontLast () { uint8 *tail = _Tail; if (empty ()) { nlwarning("BF: Try to get the front of an empty fifo!"); return 0; } if (_Rewinder != NULL && tail == _Rewinder) { #if DEBUG_FIFO nldebug("%p front rewind!", this); #endif // need to rewind tail = _Buffer; } TFifoSize s = *(TFifoSize *)tail; #if DEBUG_FIFO nldebug("%p frontLast() returns %d ", this, s, *(tail+sizeof(TFifoSize)+size-1)); #endif return *(tail+sizeof(TFifoSize)+s-1); } void CBufFIFO::front (vector &buffer) { uint8 *tmpbuffer; uint32 s; buffer.clear (); front (tmpbuffer, s); buffer.resize (s); CFastMem::memcpy (&(buffer[0]), tmpbuffer, s); /* TTicks before = CTime::getPerformanceTime (); uint8 *tail = _Tail; buffer.clear (); if (empty ()) { nlwarning("Try to get the front of an empty fifo!"); return; } _Fronted++; if (_Rewinder != NULL && tail == _Rewinder) { #if DEBUG_FIFO nldebug("%p front rewind!", this); #endif // need to rewind tail = _Buffer; } TFifoSize size = *(TFifoSize *)tail; #if DEBUG_FIFO nldebug("%p front(%d)", this, size); #endif tail += sizeof (TFifoSize); buffer.resize (size); CFastMem::memcpy (&(buffer[0]), tail, size); // stat code TTicks after = CTime::getPerformanceTime (); _FrontedTime += after - before; #if DEBUG_FIFO display (); #endif */} void CBufFIFO::front (NLMISC::CMemStream &buffer) { uint8 *tmpbuffer; uint32 s; buffer.clear (); front (tmpbuffer, s); buffer.fill (tmpbuffer, s); /* TTicks before = CTime::getPerformanceTime (); uint8 *tail = _Tail; buffer.clear (); if (empty ()) { nlwarning("Try to get the front of an empty fifo!"); return; } _Fronted++; if (_Rewinder != NULL && tail == _Rewinder) { #if DEBUG_FIFO nldebug("%p front rewind!", this); #endif // need to rewind tail = _Buffer; } TFifoSize size = *(TFifoSize *)tail; #if DEBUG_FIFO nldebug("%p front(%d)", this, size); #endif tail += sizeof (TFifoSize); //buffer.resize (size); //CFastMem::memcpy (&(buffer[0]), tail, size); buffer.fill (tail, size); // stat code TTicks after = CTime::getPerformanceTime (); _FrontedTime += after - before; #if DEBUG_FIFO display (); #endif*/ } void CBufFIFO::front (uint8 *&buffer, uint32 &s) { #if STAT_FIFO TTicks before = CTime::getPerformanceTime (); #endif uint8 *tail = _Tail; if (empty ()) { nlwarning("BF: Try to get the front of an empty fifo!"); return; } _Fronted++; if (_Rewinder != NULL && tail == _Rewinder) { #if DEBUG_FIFO nldebug("%p front rewind!", this); #endif // need to rewind tail = _Buffer; } s = *(TFifoSize *)tail; #if DEBUG_FIFO nldebug("%p front(%d)", this, s); #endif tail += sizeof (TFifoSize); #if STAT_FIFO // stat code TTicks after = CTime::getPerformanceTime (); _FrontedTime += after - before; #endif #if DEBUG_FIFO display (); #endif buffer = tail; } void CBufFIFO::clear () { _Tail = _Head = _Buffer; _Rewinder = NULL; _Empty = true; } uint32 CBufFIFO::size () { if (empty ()) { return 0; } else if (_Head == _Tail) { // buffer is full if (_Rewinder == NULL) return _BufferSize; else return (uint32)(_Rewinder - _Buffer); } else if (_Head > _Tail) { return (uint32)(_Head - _Tail); } else if (_Head < _Tail) { nlassert (_Rewinder != NULL); return (uint32)((_Rewinder - _Tail) + (_Head - _Buffer)); } nlstop; return 0; } void CBufFIFO::resize (uint32 s) { #if STAT_FIFO TTicks before = CTime::getPerformanceTime(); #endif if (s == 0) s = 100; #if DEBUG_FIFO nldebug("%p resize(%d)", this, s); #endif if (s > _BiggestBuffer) _BiggestBuffer = s; if (s < _SmallestBuffer) _SmallestBuffer = s; _Resized++; uint32 UsedSize = CBufFIFO::size(); // create a new array and copy the old in the new one if (s < _BufferSize && UsedSize > s) { // problem, we don't have enough room for putting data => don't do it nlwarning("BF: Can't resize the FIFO because there's not enough room in the new wanted buffer (%d bytes needed at least)", UsedSize); return; } uint8 *NewBuffer = new uint8[s]; if (NewBuffer == NULL) { nlerror("Not enough memory to resize the FIFO to %u bytes", s); } #ifdef NL_DEBUG // clear the message to be sure user doesn't use it anymore memset (NewBuffer, '-', s); #endif #if DEBUG_FIFO nldebug("%p new %d bytes", this, s); #endif // copy the old buffer to the new one // if _Tail == _Head => empty fifo, don't copy anything if (!empty()) { if (_Tail < _Head) { CFastMem::memcpy (NewBuffer, _Tail, UsedSize); } else if (_Tail >= _Head) { nlassert (_Rewinder != NULL); uint size1 = (uint)(_Rewinder - _Tail); CFastMem::memcpy (NewBuffer, _Tail, size1); uint size2 = (uint)(_Head - _Buffer); CFastMem::memcpy (NewBuffer + size1, _Buffer, size2); nlassert (size1+size2==UsedSize); } } // resync the circular pointer // Warning: don't invert these 2 lines position or it ll not work _Tail = NewBuffer; _Head = NewBuffer + UsedSize; _Rewinder = NULL; // delete old buffer if needed if (_Buffer != NULL) { delete []_Buffer; #if DEBUG_FIFO nldebug ("delete", this); #endif } // affect new buffer _Buffer = NewBuffer; _BufferSize = s; #if STAT_FIFO TTicks after = CTime::getPerformanceTime(); _ResizedTime += after - before; #endif #if DEBUG_FIFO display (); #endif } void CBufFIFO::displayStats (CLog *log) { log->displayNL ("%p CurrentQueueSize: %d, TotalQueueSize: %d", this, size(), _BufferSize); log->displayNL ("%p InQueue: %d", this, _Pushed - _Fronted); log->displayNL ("%p BiggestBlock: %d, SmallestBlock: %d", this, _BiggestBlock, _SmallestBlock); log->displayNL ("%p BiggestBuffer: %d, SmallestBuffer: %d", this, _BiggestBuffer, _SmallestBuffer); 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)); 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)); 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)); } void CBufFIFO::display () { int s = 64; int gran = s/30; char str[1024]; smprintf(str, 1024, "%p %p (%5d %5d) %p %p %p ", this, _Buffer, _BufferSize, CBufFIFO::size(), _Rewinder, _Tail, _Head); int i; for (i = 0; i < (sint32) _BufferSize; i+= gran) { uint8 *pos = _Buffer + i; if (_Tail >= pos && _Tail < pos + gran) { if (_Head >= pos && _Head < pos + gran) { if (_Rewinder != NULL && _Rewinder >= pos && _Rewinder < pos + gran) { strncat (str, "*", 1); } else { strncat (str, "@", 1); } } else { strncat (str, "T", 1); } } else if (_Head >= pos && _Head < pos + gran) { strncat (str, "H", 1); } else if (_Rewinder != NULL && _Rewinder >= pos && _Rewinder < pos + gran) { strncat (str, "R", 1); } else { if (strlen(str) < 1023) { uint32 p = (uint32)strlen(str); if (isprint(*pos)) str[p] = *pos; else str[p] = '$'; str[p+1] = '\0'; } } } for (; i < s; i+= gran) { strncat (str, " ", 1); } #ifdef NL_DEBUG strncat (str, "\n", 1); #else strncat (str, "\r", 1); #endif DebugLog->display (str); } bool CBufFIFO::canFit (uint32 s) { if (_Tail == _Head) { if (empty()) { // is the buffer large enough? if (_BufferSize >= s) { // reset the pointer #if DEBUG_FIFO nldebug("%p reset tail and head", this); #endif _Head = _Tail = _Buffer; return true; } else { // buffer not big enough #if DEBUG_FIFO nldebug("%p buffer full buffersize= (sint32) s) { // can fit after _Head #if DEBUG_FIFO nldebug("%p fit after", this); #endif return true; } else if (_Tail - _Buffer >= (sint32) s) { // can fit at the beginning #if DEBUG_FIFO nldebug("%p fit at beginning", this); #endif _Rewinder = _Head; #if DEBUG_FIFO nldebug("%p set the rewinder", this); #endif _Head = _Buffer; return true; } else { // can't fit #if DEBUG_FIFO nldebug("%p no room t _Head) { if (_Tail - _Head >= (sint32) s) { #if DEBUG_FIFO nldebug("%p fit t>h", this); #endif return true; } else { #if DEBUG_FIFO nldebug("%p no room t>h", this); #endif return false; } } } #ifdef BUFFIFO_TRACK_ALL_BUFFERS NLMISC_CATEGORISED_COMMAND(misc, dumpAllBuffers, "Dump all the fifo buffer", "no args") { log.displayNL("Dumping %u FIFO buffers :", CBufFIFO::_AllBuffers.size()); CBufFIFO::TAllBuffers::iterator first(CBufFIFO::_AllBuffers.begin()), last(CBufFIFO::_AllBuffers.end()); for (; first != last; ++first) { CBufFIFO *buf = *first; log.displayNL("Dumping buffer %p:", buf); buf->displayStats(&log); } return true; } #endif } // NLMISC