blob: a54983621563dfc9e14c62391da0554d130d8d71 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2/** \file /std/util/ringbuffer.c
3* Implmentiert einen Ringbuffer in LPC.
4* Ab und an hat man den Fall, dass man Ereignisse/Meldungen o.ae. hat, von
5* denen man immer genau die letzten x gespeichert und abrufbar vorhalten
6* moechte. Dieses File stellt eine Datenstruktur und dazugehoerige
7* Verwaltungsfunktionen zu Verfuegung, mit denen dies effizient moeglich ist.
8* Insbesondere umgehen diese das staendige Slicing von Arrays, mit denen
9* haeufig Ringpuffer gebaut werden. Hierbei wird das Schreiben in den Puffer
10* billiger, das Abrufen der Daten allerdings teurer. Wird der Puffer haeufiger
11* abgefragt als beschrieben, ist dies keine effiziente Loesung.
12*
13* \author Zesstra
14* \date 22.05.2008
15* \version $Id$
16*/
17/* Changelog:
18*/
19#pragma strong_types
20#pragma save_types
21#pragma no_clone
22#pragma no_inherit
23#pragma no_shadow
MG Mud User88f12472016-06-24 23:31:02 +020024#pragma range_check
25
26#include <util/ringbuffer.h>
27
28/** Standardgroesse fuer Ringbuffer.
29 */
30#define MAXCOUNT 30
31
32
33/** Struktur, die einen Ringpuffer speichert.
34 Der Puffer selber ist im wesentlichen ein Array. Hinzu kommt noch ein
35 Zaehler, der den naechsten zu ueberschreibenden Wert indiziert sowie einen
36 Modus, der die Reihenfolge beim Abrufen beshreibt:
37 MODE_FIFO: First-in-First-out (default)
38 MODE_LIFO: Last-in-First-out
39 */
40struct std_ringbuffer {
41 mixed rbuffer;
42 int mode;
43 int next;
44};
45
46/** Erzeugt einen neuen Ringbuffer und liefert ihn zurueck.
47 Die Funktion erzeugt einen neuen, leeren Ringbuffer der Groesse \a size
48 und mit Ausgabemodus \a newmode und liefert die entsprechende
49 Ringbuffer-Struktur zurueck.
50 \attention Die zurueckgelieferte Datenstruktur bitte nicht per Hand
51 manipulieren.
52 \param[in] size Groesse des zu erzeugenden Ringbuffers. Default: 30
53 \param[in] newmode Ausgabemodus des Ringbuffers: \a MODE_FIFO oder
54 \a MODE_LIFO. Default: MODE_FIFO
55 \return Der erzeugte Ringbuffer (struct vom Typ std_ringbuffer).
56 \sa ResizeRingBuffer, RingBufferPut, RingBufferGet
57 */
58protected struct std_ringbuffer CreateRingBuffer(int size, int newmode) {
59
60 struct std_ringbuffer buffer = (<std_ringbuffer>
61 rbuffer: allocate(size||MAXCOUNT, 0),
62 mode: newmode || MODE_FIFO,
63 next: 0 );
64
65 // Bei LIFO von oben anfangen.
66 if (newmode==MODE_LIFO)
67 buffer->next = sizeof(buffer->rbuffer);
68
69 return buffer;
70}
71
72/** Schreibt einen neuen Wert in den Ringbuffer.
73 Diese Funktion schreibt einen neuen Wert in den Ringbuffer \a buffer und
74 loescht dabei ggf. den aeltesten Wert im Puffer.
75 \param[in,out] buffer zu aendernder Ringbuffer
76 \param[in] val hinzuzufuegender Wert
77 \sa CreateRingBuffer, ResizeRingBuffer, RingBufferGet
78 */
79protected void RingBufferPut(struct std_ringbuffer buffer, mixed val) {
80 int next = buffer->next;
81 // je nach Ausgabemodus von oben nach unten oder von unten nach oben die
82 // Werte reinschreiben.
83 switch(buffer->mode) {
84 case MODE_LIFO:
85 // next ist hier eigentlich ein 'last'. Dekrementieren und dann zum
86 // Indizieren benutzen.
87 buffer->rbuffer[--next] = val;
88 // wenn man gerade in Element 0 geschrieben hat, ist naechstes Element
89 // sizeof()-1, d.h. sizeof() als next vermerken.
90 next ||= sizeof(buffer->rbuffer);
91 break;
92 default:
93 // Wert schreiben und next inkrementieren.
94 buffer->rbuffer[next++] = val;
95 // wird sizeof() erreicht, ist das naechste Element 0. Etwas schneller
96 // waere ein Modulo mit der Buffergroesse direkt bei der Indizierung
97 // oben, aber dann kann es u.U. durch einen numerischen Overflow buggen.
98 if (next==sizeof(buffer->rbuffer))
99 next = 0;
100 }
101 buffer->next = next;
102}
103
104/** Liefert ein Array mit allen Werten des Ringbuffers \a buffer.
105 Das zurueckgelieferte Array enthaelt alle Daten des Ringbuffers in der
106 beim Erstellen des Puffers gewuenschten Reihenfolge (s. MODE_FIFO,
107 MODE_LIFO).
108 Ist der Puffer noch nicht vollstaendig gefuellt, enthalten die bisher noch
109 nie beschriebenen Elemente 0.
110 \param[in] buffer Ringpuffer, dessen Daten ausgeben werden sollen.
111 \return Array mit Daten des Puffers.
112 \sa CreateRingBuffer, RingBufferPut, ResizeRingBuffer
113 */
114protected mixed RingBufferGet(struct std_ringbuffer buffer) {
115 int size = sizeof(buffer->rbuffer);
116 int next = buffer->next;
117 mixed rbuffer = buffer->rbuffer;
118
119 switch(buffer->mode) {
120 case MODE_LIFO:
121 // der hintere Teil enthaelt die neueren Daten. Die kommen zuerst.
122 // Wenn next == sizeof(), dann kann das interne Array einfach kopiert
123 // werden.
124 if (next == size)
125 return copy(rbuffer);
126 else
127 return rbuffer[next .. size-1] + rbuffer[0 .. next-1];
128 default:
129 // der vordere Teil enthaelt die neueren Daten. Also zuerst den hinteren
130 // ausgeben, dann den vorderen.
131 // Wenn next 0, kann das interne Array einfach kopiert werden. Sonst
132 // muss es zusammengesetzt werden.
133 if (next)
134 return rbuffer[next .. size-1] + rbuffer[0 .. next-1];
135 else
136 return copy(rbuffer);
137 }
MG Mud User88f12472016-06-24 23:31:02 +0200138}
139
140/** Erzeugt einen neuen Ringbuffer der Groesse \a size und dem gleichen Modus
141 * wie \a buf, der dieselben Daten enthaelt wie \a buf.
142 * Diese Funktion erzeugt einen neuen Ringbuffer und kopiert alle Daten aus
143 * dem alten Puffer in den neuen um.
144 \param[in] buf alter Ringbuffer
145 \param[in] size gewuenschte neue Groesse
146 \return Liefert neuen Ringbuffer mit gewuenschten Groesse.
147 \sa CreateRingBuffer, RingBufferPut, RingBufferGet
148 \attention Wird der Ringbuffer verkleinert, kommt es zum Verlust der
149 aeltesten Daten, die nicht mehr in den neuen hineinpassen.
150 \todo Effizientere Implementation.
151 */
152protected struct std_ringbuffer ResizeRingBuffer(struct std_ringbuffer buf,
153 int size) {
154 // neuen Ringbuffer anlegen.
155 struct std_ringbuffer nbuf = CreateRingBuffer(size, buf->mode);
156 // und alle daten aus dem alten wieder reinstopfen.
157 // ja, das geht effizienter, hab ich gerade aber keinen Bock drauf. Die
158 // Funktion wird vermutlich eh sehr selten gerufen.
159 foreach(mixed val: RingBufferGet(buf))
160 RingBufferPut(nbuf, val);
161 // neuen Buffer zurueckgeben. Der alte bleibt unveraendert!
162 return nbuf;
163}
164
165/* halbfertige Implementation mit Arrays.
166#define RBUFFER 0
167#define MODE 1
168#define COUNTER 2
169
170protected mixed CreateRingBuffer(int size, int mode) {
171 mixed buffer = ({ allocate(size||MAXCOUNT, 0),
172 mode || MODE_FIFO,
173 0 });
174 if (mode==MODE_LIFO)
175 buffer[COUNTER] = sizeof(buffer[RBUFFER]);
176 return buffer;
177}
178
179protected void RingBufferPut(mixed buffer, mixed val) {
180 int counter = buffer[COUNTER];
181 switch(mode) {
182 case MODE_LIFO:
183 rbuffer[--counter] = val;
184 counter ||= sizeof(rbuffer);
185 break;
186 default:
187 rbuffer[(counter++)%sizeof(rbuffer)] = val;
188 }
189 buffer[COUNTER]=counter;
190}
191
192protected mixed RingBufferGet(mixed buffer) {
193 int size=sizeof(rbuffer);
194
195 switch(mode) {
196 case MODE_LIFO:
197 return rbuffer[counter..size] + rbuffer[0..counter-1];
198 default:
199 if (counter%size)
200 return rbuffer[(counter%size) .. size-1]
201 + rbuffer[0..(counter%size)-1];
202 else
203 return copy(rbuffer);
204 }
205}
206
207protected mixed test() {return rbuffer;}
208*/