/** * \file bufferq.c * * \brief Code for managing queues of buffers, a handy data structure. * * */ #include "copyrite.h" #include "config.h" #include <stdio.h> #ifdef I_UNISTD #include <unistd.h> #endif #include <string.h> #include <stddef.h> #include <stdarg.h> #include <stdlib.h> #ifdef I_SYS_TIME #include <sys/time.h> #ifdef TIME_WITH_SYS_TIME #include <time.h> #endif #else #include <time.h> #endif #ifdef I_SYS_TYPES #include <sys/types.h> #endif #include "conf.h" #include "externs.h" #include "flags.h" #include "dbdefs.h" #include "bufferq.h" #include "mymalloc.h" #include "log.h" #include "confmagic.h" #define BUFFERQLINEOVERHEAD (2*sizeof(int)+sizeof(time_t)+sizeof(dbref)) static void shift_bufferq(BUFFERQ *bq, int space_needed); /** Add data to a buffer queue. * \param bq pointer to buffer queue. * \param type caller-specific integer * \param player caller-specific dbref * \param msg caller-specific string to add. */ void add_to_bufferq(BUFFERQ *bq, int type, dbref player, const char *msg) { int len = strlen(msg); int room = len + 1 + BUFFERQLINEOVERHEAD; if (!bq) return; if (room > bq->buffer_size) return; if ((bq->buffer_end > bq->buffer) && ((bq->buffer_size - (bq->buffer_end - bq->buffer)) < room)) shift_bufferq(bq, room); memcpy(bq->buffer_end, &len, sizeof(len)); bq->buffer_end += sizeof(len); memcpy(bq->buffer_end, &player, sizeof(player)); bq->buffer_end += sizeof(player); memcpy(bq->buffer_end, &type, sizeof(type)); bq->buffer_end += sizeof(type); memcpy(bq->buffer_end, &mudtime, sizeof(time_t)); bq->buffer_end += sizeof(time_t); memcpy(bq->buffer_end, msg, len + 1); bq->buffer_end += len + 1; strcpy(bq->last_string, msg); bq->last_type = type; bq->num_buffered++; } static void shift_bufferq(BUFFERQ *bq, int space_needed) { char *p = bq->buffer; int size, jump; int skipped = 0; while ((space_needed > 0) && (p < bq->buffer_end)) { /* First 4 bytes is the size of the first string, not including \0 */ memcpy(&size, p, sizeof(size)); /* Jump to the start of the next string */ jump = size + BUFFERQLINEOVERHEAD + 1; p += jump; space_needed -= jump; skipped++; } if ((p != bq->buffer_end) && (space_needed > 0)) { /* Not good. We couldn't get the space we needed even after we * emptied the buffer. This should never happen, but if it does, * we'll just log a fault and do nothing. */ do_rawlog(LT_ERR, "Unable to get enough buffer queue space"); return; } /* Shift everything here and after up to the front * At this point, p may be pointing at the very end of the buffer, * in which case, we just move it to the front with no shifting. */ if (p < bq->buffer_end) memmove(bq->buffer, p, bq->buffer_end - p); bq->buffer_end -= (p - bq->buffer); bq->num_buffered -= skipped; } /** Allocate memory for a buffer queue to hold a given number of lines. * \param bq pointer to buffer queue. * \param lines lines to allocate for buffer queue. * \retval address of allocated buffer queue. */ BUFFERQ * allocate_bufferq(int lines) { BUFFERQ *bq; int bytes = lines * (BUFFER_LEN + BUFFERQLINEOVERHEAD); bq = mush_malloc(sizeof(BUFFERQ), "bufferq"); bq->buffer = mush_malloc(bytes, "bufferq.buffer"); *bq->buffer = '\0'; bq->buffer_end = bq->buffer; bq->num_buffered = 0; bq->buffer_size = bytes; strcpy(bq->last_string, ""); bq->last_type = 0; return bq; } /** Free memory of a buffer queue. * \param bq pointer to buffer queue. */ void free_bufferq(BUFFERQ *bq) { if (!bq) return; if (bq->buffer) mush_free(bq->buffer, "bufferq.buffer"); mush_free(bq, "bufferq"); } /** Reallocate a buffer queue (to change its size) * \param bq pointer to buffer queue. * \param lines new number of lines to store in buffer queue. * \retval address of reallocated buffer queue. */ BUFFERQ * reallocate_bufferq(BUFFERQ *bq, int lines) { char *newbuff; ptrdiff_t bufflen; int bytes = lines * (BUFFER_LEN + 2 * BUFFERQLINEOVERHEAD); /* If we were accidentally called without a buffer, deal */ if (!bq) { return allocate_bufferq(lines); } /* Are we not changing size? */ if (bq->buffer_size == bytes) return bq; if (bq->buffer_size > bytes) { /* Shrinking the buffer */ if ((bq->buffer_end - bq->buffer) >= bytes) shift_bufferq(bq, bq->buffer_end - bq->buffer - bytes); } bufflen = bq->buffer_end - bq->buffer; newbuff = realloc(bq->buffer, bytes); if (newbuff) { bq->buffer = newbuff; bq->buffer_end = bq->buffer + bufflen; bq->buffer_size = bytes; } return bq; } /** Iterate through messages in a bufferq. * This function returns the next message in a bufferq, given * a pointer to the start of entry (which is modified). * It returns NULL when there are no more messages to get. * Call this in a loop to get all messages; do not intersperse * with calls to insert messages! * \param bq pointer to buffer queue structure. * \param p address of pointer to track start of next entry. If that's * the address of a null pointer, reset to beginning. * \param player address of pointer to return player data in * \param type address of pointer to return type value in. * \param timestamp address of pointer to return timestamp in. * \return next message text or NULL if no more. */ char * iter_bufferq(BUFFERQ *bq, char **p, dbref *player, int *type, time_t * timestamp) { static char tbuf1[BUFFER_LEN]; int size; if (!p || !bq || !bq->buffer || (bq->buffer == bq->buffer_end)) return NULL; if (*p == bq->buffer_end) return NULL; if (!*p) *p = bq->buffer; /* Reset to beginning */ memcpy(&size, *p, sizeof(size)); *p += sizeof(size); memcpy(player, *p, sizeof(dbref)); *p += sizeof(dbref); memcpy(type, *p, sizeof(int)); *p += sizeof(int); memcpy(timestamp, *p, sizeof(time_t)); *p += sizeof(time_t); memcpy(tbuf1, *p, size + 1); *p += size + 1; return tbuf1; } /** Size of bufferq buffer in lines. * \param bq pointer to buffer queue. * \return size of buffer queue in lines */ int bufferq_lines(BUFFERQ *bq) { if (bq && bq->buffer) return bq->buffer_size / (BUFFER_LEN + BUFFERQLINEOVERHEAD); else return 0; } /** Is a buffer queue empty? * \param bq pointer to buffer queue. * \retval 1 the buffer queue is empty (has no messages). * \retval 0 the buffer queue is not empty (has messages). */ int isempty_bufferq(BUFFERQ *bq) { if (!bq || !bq->buffer) return 1; if (bq->buffer == bq->buffer_end) return 1; return 0; }