1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "nel/misc/time_nl.h"
22 #include "nel/misc/command.h"
23 #include "nel/misc/buf_fifo.h"
29 // if 0, don't stat the time of different function
38 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
39 CBufFIFO::TAllBuffers
CBufFIFO::_AllBuffers
;
43 CBufFIFO::CBufFIFO() : _Buffer(NULL
), _BufferSize(0), _Empty(true), _Head(NULL
), _Tail(NULL
), _Rewinder(NULL
)
45 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
46 _AllBuffers
.insert(this);
51 _SmallestBlock
= 999999999;
53 _SmallestBuffer
= 999999999;
64 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
65 _AllBuffers
.erase(this);
72 nldebug("%p delete", this);
77 void CBufFIFO::push (const uint8
*buffer
, uint32 s
)
79 // if the buffer is more than 1 meg, there s surely a problem, no?
80 // nlassert( buffer.size() < 1000000 ); // size check in debug mode
83 TTicks before
= CTime::getPerformanceTime();
87 nldebug("%p push(%d)", this, s
);
90 nlassert(s
> 0 && s
< pow(2.0, static_cast<double>(sizeof(TFifoSize
)*8)));
93 if (s
> _BiggestBlock
) _BiggestBlock
= s
;
94 if (s
< _SmallestBlock
) _SmallestBlock
= s
;
97 while (!canFit (s
+ sizeof (TFifoSize
)))
99 resize(_BufferSize
* 2);
102 *(TFifoSize
*)_Head
= s
;
103 _Head
+= sizeof(TFifoSize
);
105 CFastMem::memcpy(_Head
, buffer
, s
);
113 TTicks after
= CTime::getPerformanceTime();
114 _PushedTime
+= after
- before
;
122 void CBufFIFO::push(const std::vector
<uint8
> &buffer1
, const std::vector
<uint8
> &buffer2
)
125 TTicks before
= CTime::getPerformanceTime();
128 TFifoSize s
= (TFifoSize
)(buffer1
.size() + buffer2
.size());
131 nldebug("%p push2(%d)", this, s
);
134 nlassert((buffer1
.size() + buffer2
.size ()) > 0 && (buffer1
.size() + buffer2
.size ()) < pow(2.0, static_cast<double>(sizeof(TFifoSize
)*8)));
136 // avoid too big fifo
137 if (this->size() > 10000000)
139 throw Exception ("CBufFIFO::push(): stack full (more than 10mb)");
144 if (s
> _BiggestBlock
) _BiggestBlock
= s
;
145 if (s
< _SmallestBlock
) _SmallestBlock
= s
;
149 // resize while the buffer is enough big to accept the block
150 while (!canFit (s
+ sizeof (TFifoSize
)))
152 resize(_BufferSize
* 2);
155 // store the size of the block
156 *(TFifoSize
*)_Head
= s
;
157 _Head
+= sizeof(TFifoSize
);
159 // store the block itself
160 CFastMem::memcpy(_Head
, &(buffer1
[0]), buffer1
.size());
161 CFastMem::memcpy(_Head
+ buffer1
.size(), &(buffer2
[0]), buffer2
.size());
168 TTicks after
= CTime::getPerformanceTime();
169 _PushedTime
+= after
- before
;
177 void CBufFIFO::pop ()
181 nlwarning("BF: Try to pop an empty fifo!");
185 if (_Rewinder
!= NULL
&& _Tail
== _Rewinder
)
188 nldebug("%p pop rewind!", this);
196 TFifoSize s
= *(TFifoSize
*)_Tail
;
199 nldebug("%p pop(%d)", this, s
);
203 // clear the message to be sure user doesn't use it anymore
204 memset (_Tail
, '-', s
+ sizeof (TFifoSize
));
207 _Tail
+= s
+ sizeof (TFifoSize
);
209 if (_Tail
== _Head
) _Empty
= true;
216 uint8
CBufFIFO::frontLast ()
222 nlwarning("BF: Try to get the front of an empty fifo!");
226 if (_Rewinder
!= NULL
&& tail
== _Rewinder
)
229 nldebug("%p front rewind!", this);
236 TFifoSize s
= *(TFifoSize
*)tail
;
239 nldebug("%p frontLast() returns %d ", this, s
, *(tail
+sizeof(TFifoSize
)+size
-1));
242 return *(tail
+sizeof(TFifoSize
)+s
-1);
246 void CBufFIFO::front (vector
<uint8
> &buffer
)
253 front (tmpbuffer
, s
);
257 CFastMem::memcpy (&(buffer
[0]), tmpbuffer
, s
);
259 /* TTicks before = CTime::getPerformanceTime ();
267 nlwarning("Try to get the front of an empty fifo!");
273 if (_Rewinder != NULL && tail == _Rewinder)
276 nldebug("%p front rewind!", this);
283 TFifoSize size = *(TFifoSize *)tail;
286 nldebug("%p front(%d)", this, size);
289 tail += sizeof (TFifoSize);
291 buffer.resize (size);
293 CFastMem::memcpy (&(buffer[0]), tail, size);
296 TTicks after = CTime::getPerformanceTime ();
297 _FrontedTime += after - before;
305 void CBufFIFO::front (NLMISC::CMemStream
&buffer
)
312 front (tmpbuffer
, s
);
314 buffer
.fill (tmpbuffer
, s
);
317 TTicks before = CTime::getPerformanceTime ();
325 nlwarning("Try to get the front of an empty fifo!");
331 if (_Rewinder != NULL && tail == _Rewinder)
334 nldebug("%p front rewind!", this);
341 TFifoSize size = *(TFifoSize *)tail;
344 nldebug("%p front(%d)", this, size);
347 tail += sizeof (TFifoSize);
349 //buffer.resize (size);
350 //CFastMem::memcpy (&(buffer[0]), tail, size);
352 buffer.fill (tail, size);
355 TTicks after = CTime::getPerformanceTime ();
356 _FrontedTime += after - before;
363 void CBufFIFO::front (uint8
*&buffer
, uint32
&s
)
366 TTicks before
= CTime::getPerformanceTime ();
373 nlwarning("BF: Try to get the front of an empty fifo!");
379 if (_Rewinder
!= NULL
&& tail
== _Rewinder
)
382 nldebug("%p front rewind!", this);
389 s
= *(TFifoSize
*)tail
;
392 nldebug("%p front(%d)", this, s
);
395 tail
+= sizeof (TFifoSize
);
399 TTicks after
= CTime::getPerformanceTime ();
400 _FrontedTime
+= after
- before
;
412 void CBufFIFO::clear ()
414 _Tail
= _Head
= _Buffer
;
419 uint32
CBufFIFO::size ()
425 else if (_Head
== _Tail
)
428 if (_Rewinder
== NULL
)
431 return (uint32
)(_Rewinder
- _Buffer
);
433 else if (_Head
> _Tail
)
435 return (uint32
)(_Head
- _Tail
);
437 else if (_Head
< _Tail
)
439 nlassert (_Rewinder
!= NULL
);
440 return (uint32
)((_Rewinder
- _Tail
) + (_Head
- _Buffer
));
446 void CBufFIFO::resize (uint32 s
)
449 TTicks before
= CTime::getPerformanceTime();
455 nldebug("%p resize(%d)", this, s
);
458 if (s
> _BiggestBuffer
) _BiggestBuffer
= s
;
459 if (s
< _SmallestBuffer
) _SmallestBuffer
= s
;
463 uint32 UsedSize
= CBufFIFO::size();
465 // create a new array and copy the old in the new one
466 if (s
< _BufferSize
&& UsedSize
> s
)
468 // problem, we don't have enough room for putting data => don't do it
469 nlwarning("BF: Can't resize the FIFO because there's not enough room in the new wanted buffer (%d bytes needed at least)", UsedSize
);
473 uint8
*NewBuffer
= new uint8
[s
];
474 if (NewBuffer
== NULL
)
476 nlerror("Not enough memory to resize the FIFO to %u bytes", s
);
479 // clear the message to be sure user doesn't use it anymore
480 memset (NewBuffer
, '-', s
);
484 nldebug("%p new %d bytes", this, s
);
487 // copy the old buffer to the new one
488 // if _Tail == _Head => empty fifo, don't copy anything
493 CFastMem::memcpy (NewBuffer
, _Tail
, UsedSize
);
495 else if (_Tail
>= _Head
)
497 nlassert (_Rewinder
!= NULL
);
499 uint size1
= (uint
)(_Rewinder
- _Tail
);
500 CFastMem::memcpy (NewBuffer
, _Tail
, size1
);
501 uint size2
= (uint
)(_Head
- _Buffer
);
502 CFastMem::memcpy (NewBuffer
+ size1
, _Buffer
, size2
);
504 nlassert (size1
+size2
==UsedSize
);
508 // resync the circular pointer
509 // Warning: don't invert these 2 lines position or it ll not work
511 _Head
= NewBuffer
+ UsedSize
;
514 // delete old buffer if needed
519 nldebug ("delete", this);
528 TTicks after
= CTime::getPerformanceTime();
529 _ResizedTime
+= after
- before
;
537 void CBufFIFO::displayStats (CLog
*log
)
539 log
->displayNL ("%p CurrentQueueSize: %d, TotalQueueSize: %d", this, size(), _BufferSize
);
540 log
->displayNL ("%p InQueue: %d", this, _Pushed
- _Fronted
);
542 log
->displayNL ("%p BiggestBlock: %d, SmallestBlock: %d", this, _BiggestBlock
, _SmallestBlock
);
543 log
->displayNL ("%p BiggestBuffer: %d, SmallestBuffer: %d", this, _BiggestBuffer
, _SmallestBuffer
);
544 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));
545 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));
546 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));
549 void CBufFIFO::display ()
556 smprintf(str
, 1024, "%p %p (%5d %5d) %p %p %p ", this, _Buffer
, _BufferSize
, CBufFIFO::size(), _Rewinder
, _Tail
, _Head
);
559 for (i
= 0; i
< (sint32
) _BufferSize
; i
+= gran
)
561 uint8
*pos
= _Buffer
+ i
;
562 if (_Tail
>= pos
&& _Tail
< pos
+ gran
)
564 if (_Head
>= pos
&& _Head
< pos
+ gran
)
566 if (_Rewinder
!= NULL
&& _Rewinder
>= pos
&& _Rewinder
< pos
+ gran
)
568 strncat (str
, "*", 1);
572 strncat (str
, "@", 1);
577 strncat (str
, "T", 1);
580 else if (_Head
>= pos
&& _Head
< pos
+ gran
)
582 strncat (str
, "H", 1);
584 else if (_Rewinder
!= NULL
&& _Rewinder
>= pos
&& _Rewinder
< pos
+ gran
)
586 strncat (str
, "R", 1);
590 if (strlen(str
) < 1023)
592 uint32 p
= (uint32
)strlen(str
);
603 for (; i
< s
; i
+= gran
)
605 strncat (str
, " ", 1);
608 strncat (str
, "\n", 1);
610 strncat (str
, "\r", 1);
612 DebugLog
->display (str
);
615 bool CBufFIFO::canFit (uint32 s
)
621 // is the buffer large enough?
622 if (_BufferSize
>= s
)
626 nldebug("%p reset tail and head", this);
628 _Head
= _Tail
= _Buffer
;
633 // buffer not big enough
635 nldebug("%p buffer full buffersize<size", this);
644 nldebug("%p buffer full h=t", this);
649 else if (_Tail
< _Head
)
651 if (_Buffer
+ _BufferSize
- _Head
>= (sint32
) s
)
653 // can fit after _Head
655 nldebug("%p fit after", this);
659 else if (_Tail
- _Buffer
>= (sint32
) s
)
661 // can fit at the beginning
663 nldebug("%p fit at beginning", this);
667 nldebug("%p set the rewinder", this);
676 nldebug("%p no room t<h", this);
681 else // the last case is : if (_Tail > _Head)
683 if (_Tail
- _Head
>= (sint32
) s
)
686 nldebug("%p fit t>h", this);
693 nldebug("%p no room t>h", this);
700 #ifdef BUFFIFO_TRACK_ALL_BUFFERS
701 NLMISC_CATEGORISED_COMMAND(misc
, dumpAllBuffers
, "Dump all the fifo buffer", "no args")
703 log
.displayNL("Dumping %u FIFO buffers :", CBufFIFO::_AllBuffers
.size());
705 CBufFIFO::TAllBuffers::iterator
first(CBufFIFO::_AllBuffers
.begin()), last(CBufFIFO::_AllBuffers
.end());
706 for (; first
!= last
; ++first
)
708 CBufFIFO
*buf
= *first
;
710 log
.displayNL("Dumping buffer %p:", buf
);
712 buf
->displayStats(&log
);