| // MorgenGrauen MUDlib |
| /** \file /std/util/ringbuffer.c |
| * Implmentiert einen Ringbuffer in LPC. |
| * Ab und an hat man den Fall, dass man Ereignisse/Meldungen o.ae. hat, von |
| * denen man immer genau die letzten x gespeichert und abrufbar vorhalten |
| * moechte. Dieses File stellt eine Datenstruktur und dazugehoerige |
| * Verwaltungsfunktionen zu Verfuegung, mit denen dies effizient moeglich ist. |
| * Insbesondere umgehen diese das staendige Slicing von Arrays, mit denen |
| * haeufig Ringpuffer gebaut werden. Hierbei wird das Schreiben in den Puffer |
| * billiger, das Abrufen der Daten allerdings teurer. Wird der Puffer haeufiger |
| * abgefragt als beschrieben, ist dies keine effiziente Loesung. |
| * |
| * \author Zesstra |
| * \date 22.05.2008 |
| * \version $Id$ |
| */ |
| /* Changelog: |
| */ |
| #pragma strong_types |
| #pragma save_types |
| #pragma no_clone |
| #pragma no_inherit |
| #pragma no_shadow |
| #pragma pedantic |
| #pragma range_check |
| |
| #include <util/ringbuffer.h> |
| |
| /** Standardgroesse fuer Ringbuffer. |
| */ |
| #define MAXCOUNT 30 |
| |
| |
| /** Struktur, die einen Ringpuffer speichert. |
| Der Puffer selber ist im wesentlichen ein Array. Hinzu kommt noch ein |
| Zaehler, der den naechsten zu ueberschreibenden Wert indiziert sowie einen |
| Modus, der die Reihenfolge beim Abrufen beshreibt: |
| MODE_FIFO: First-in-First-out (default) |
| MODE_LIFO: Last-in-First-out |
| */ |
| struct std_ringbuffer { |
| mixed rbuffer; |
| int mode; |
| int next; |
| }; |
| |
| /** Erzeugt einen neuen Ringbuffer und liefert ihn zurueck. |
| Die Funktion erzeugt einen neuen, leeren Ringbuffer der Groesse \a size |
| und mit Ausgabemodus \a newmode und liefert die entsprechende |
| Ringbuffer-Struktur zurueck. |
| \attention Die zurueckgelieferte Datenstruktur bitte nicht per Hand |
| manipulieren. |
| \param[in] size Groesse des zu erzeugenden Ringbuffers. Default: 30 |
| \param[in] newmode Ausgabemodus des Ringbuffers: \a MODE_FIFO oder |
| \a MODE_LIFO. Default: MODE_FIFO |
| \return Der erzeugte Ringbuffer (struct vom Typ std_ringbuffer). |
| \sa ResizeRingBuffer, RingBufferPut, RingBufferGet |
| */ |
| protected struct std_ringbuffer CreateRingBuffer(int size, int newmode) { |
| |
| struct std_ringbuffer buffer = (<std_ringbuffer> |
| rbuffer: allocate(size||MAXCOUNT, 0), |
| mode: newmode || MODE_FIFO, |
| next: 0 ); |
| |
| // Bei LIFO von oben anfangen. |
| if (newmode==MODE_LIFO) |
| buffer->next = sizeof(buffer->rbuffer); |
| |
| return buffer; |
| } |
| |
| /** Schreibt einen neuen Wert in den Ringbuffer. |
| Diese Funktion schreibt einen neuen Wert in den Ringbuffer \a buffer und |
| loescht dabei ggf. den aeltesten Wert im Puffer. |
| \param[in,out] buffer zu aendernder Ringbuffer |
| \param[in] val hinzuzufuegender Wert |
| \sa CreateRingBuffer, ResizeRingBuffer, RingBufferGet |
| */ |
| protected void RingBufferPut(struct std_ringbuffer buffer, mixed val) { |
| int next = buffer->next; |
| // je nach Ausgabemodus von oben nach unten oder von unten nach oben die |
| // Werte reinschreiben. |
| switch(buffer->mode) { |
| case MODE_LIFO: |
| // next ist hier eigentlich ein 'last'. Dekrementieren und dann zum |
| // Indizieren benutzen. |
| buffer->rbuffer[--next] = val; |
| // wenn man gerade in Element 0 geschrieben hat, ist naechstes Element |
| // sizeof()-1, d.h. sizeof() als next vermerken. |
| next ||= sizeof(buffer->rbuffer); |
| break; |
| default: |
| // Wert schreiben und next inkrementieren. |
| buffer->rbuffer[next++] = val; |
| // wird sizeof() erreicht, ist das naechste Element 0. Etwas schneller |
| // waere ein Modulo mit der Buffergroesse direkt bei der Indizierung |
| // oben, aber dann kann es u.U. durch einen numerischen Overflow buggen. |
| if (next==sizeof(buffer->rbuffer)) |
| next = 0; |
| } |
| buffer->next = next; |
| } |
| |
| /** Liefert ein Array mit allen Werten des Ringbuffers \a buffer. |
| Das zurueckgelieferte Array enthaelt alle Daten des Ringbuffers in der |
| beim Erstellen des Puffers gewuenschten Reihenfolge (s. MODE_FIFO, |
| MODE_LIFO). |
| Ist der Puffer noch nicht vollstaendig gefuellt, enthalten die bisher noch |
| nie beschriebenen Elemente 0. |
| \param[in] buffer Ringpuffer, dessen Daten ausgeben werden sollen. |
| \return Array mit Daten des Puffers. |
| \sa CreateRingBuffer, RingBufferPut, ResizeRingBuffer |
| */ |
| protected mixed RingBufferGet(struct std_ringbuffer buffer) { |
| int size = sizeof(buffer->rbuffer); |
| int next = buffer->next; |
| mixed rbuffer = buffer->rbuffer; |
| |
| switch(buffer->mode) { |
| case MODE_LIFO: |
| // der hintere Teil enthaelt die neueren Daten. Die kommen zuerst. |
| // Wenn next == sizeof(), dann kann das interne Array einfach kopiert |
| // werden. |
| if (next == size) |
| return copy(rbuffer); |
| else |
| return rbuffer[next .. size-1] + rbuffer[0 .. next-1]; |
| default: |
| // der vordere Teil enthaelt die neueren Daten. Also zuerst den hinteren |
| // ausgeben, dann den vorderen. |
| // Wenn next 0, kann das interne Array einfach kopiert werden. Sonst |
| // muss es zusammengesetzt werden. |
| if (next) |
| return rbuffer[next .. size-1] + rbuffer[0 .. next-1]; |
| else |
| return copy(rbuffer); |
| } |
| return 0; |
| } |
| |
| /** Erzeugt einen neuen Ringbuffer der Groesse \a size und dem gleichen Modus |
| * wie \a buf, der dieselben Daten enthaelt wie \a buf. |
| * Diese Funktion erzeugt einen neuen Ringbuffer und kopiert alle Daten aus |
| * dem alten Puffer in den neuen um. |
| \param[in] buf alter Ringbuffer |
| \param[in] size gewuenschte neue Groesse |
| \return Liefert neuen Ringbuffer mit gewuenschten Groesse. |
| \sa CreateRingBuffer, RingBufferPut, RingBufferGet |
| \attention Wird der Ringbuffer verkleinert, kommt es zum Verlust der |
| aeltesten Daten, die nicht mehr in den neuen hineinpassen. |
| \todo Effizientere Implementation. |
| */ |
| protected struct std_ringbuffer ResizeRingBuffer(struct std_ringbuffer buf, |
| int size) { |
| // neuen Ringbuffer anlegen. |
| struct std_ringbuffer nbuf = CreateRingBuffer(size, buf->mode); |
| // und alle daten aus dem alten wieder reinstopfen. |
| // ja, das geht effizienter, hab ich gerade aber keinen Bock drauf. Die |
| // Funktion wird vermutlich eh sehr selten gerufen. |
| foreach(mixed val: RingBufferGet(buf)) |
| RingBufferPut(nbuf, val); |
| // neuen Buffer zurueckgeben. Der alte bleibt unveraendert! |
| return nbuf; |
| } |
| |
| /* halbfertige Implementation mit Arrays. |
| #define RBUFFER 0 |
| #define MODE 1 |
| #define COUNTER 2 |
| |
| protected mixed CreateRingBuffer(int size, int mode) { |
| mixed buffer = ({ allocate(size||MAXCOUNT, 0), |
| mode || MODE_FIFO, |
| 0 }); |
| if (mode==MODE_LIFO) |
| buffer[COUNTER] = sizeof(buffer[RBUFFER]); |
| return buffer; |
| } |
| |
| protected void RingBufferPut(mixed buffer, mixed val) { |
| int counter = buffer[COUNTER]; |
| switch(mode) { |
| case MODE_LIFO: |
| rbuffer[--counter] = val; |
| counter ||= sizeof(rbuffer); |
| break; |
| default: |
| rbuffer[(counter++)%sizeof(rbuffer)] = val; |
| } |
| buffer[COUNTER]=counter; |
| } |
| |
| protected mixed RingBufferGet(mixed buffer) { |
| int size=sizeof(rbuffer); |
| |
| switch(mode) { |
| case MODE_LIFO: |
| return rbuffer[counter..size] + rbuffer[0..counter-1]; |
| default: |
| if (counter%size) |
| return rbuffer[(counter%size) .. size-1] |
| + rbuffer[0..(counter%size)-1]; |
| else |
| return copy(rbuffer); |
| } |
| } |
| |
| protected mixed test() {return rbuffer;} |
| */ |