blob: b7143af632eb218038a5d959fb90a1c17d5dc829 [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 }
138 return 0;
139}
140
141/** Erzeugt einen neuen Ringbuffer der Groesse \a size und dem gleichen Modus
142 * wie \a buf, der dieselben Daten enthaelt wie \a buf.
143 * Diese Funktion erzeugt einen neuen Ringbuffer und kopiert alle Daten aus
144 * dem alten Puffer in den neuen um.
145 \param[in] buf alter Ringbuffer
146 \param[in] size gewuenschte neue Groesse
147 \return Liefert neuen Ringbuffer mit gewuenschten Groesse.
148 \sa CreateRingBuffer, RingBufferPut, RingBufferGet
149 \attention Wird der Ringbuffer verkleinert, kommt es zum Verlust der
150 aeltesten Daten, die nicht mehr in den neuen hineinpassen.
151 \todo Effizientere Implementation.
152 */
153protected struct std_ringbuffer ResizeRingBuffer(struct std_ringbuffer buf,
154 int size) {
155 // neuen Ringbuffer anlegen.
156 struct std_ringbuffer nbuf = CreateRingBuffer(size, buf->mode);
157 // und alle daten aus dem alten wieder reinstopfen.
158 // ja, das geht effizienter, hab ich gerade aber keinen Bock drauf. Die
159 // Funktion wird vermutlich eh sehr selten gerufen.
160 foreach(mixed val: RingBufferGet(buf))
161 RingBufferPut(nbuf, val);
162 // neuen Buffer zurueckgeben. Der alte bleibt unveraendert!
163 return nbuf;
164}
165
166/* halbfertige Implementation mit Arrays.
167#define RBUFFER 0
168#define MODE 1
169#define COUNTER 2
170
171protected mixed CreateRingBuffer(int size, int mode) {
172 mixed buffer = ({ allocate(size||MAXCOUNT, 0),
173 mode || MODE_FIFO,
174 0 });
175 if (mode==MODE_LIFO)
176 buffer[COUNTER] = sizeof(buffer[RBUFFER]);
177 return buffer;
178}
179
180protected void RingBufferPut(mixed buffer, mixed val) {
181 int counter = buffer[COUNTER];
182 switch(mode) {
183 case MODE_LIFO:
184 rbuffer[--counter] = val;
185 counter ||= sizeof(rbuffer);
186 break;
187 default:
188 rbuffer[(counter++)%sizeof(rbuffer)] = val;
189 }
190 buffer[COUNTER]=counter;
191}
192
193protected mixed RingBufferGet(mixed buffer) {
194 int size=sizeof(rbuffer);
195
196 switch(mode) {
197 case MODE_LIFO:
198 return rbuffer[counter..size] + rbuffer[0..counter-1];
199 default:
200 if (counter%size)
201 return rbuffer[(counter%size) .. size-1]
202 + rbuffer[0..(counter%size)-1];
203 else
204 return copy(rbuffer);
205 }
206}
207
208protected mixed test() {return rbuffer;}
209*/