1 #ifndef ROLLBUF_H_ 2 #define ROLLBUF_H_ 3 4 #include <sys/types.h> // ssize_t 5 #include <stdio.h> // for size_t, FILE 6 7 /* Example of a rotating, and reallocating buffer. A separate thread has 8 * to fetch the data from the real source and fill the buffer. This way, 9 * reads block as little as possible -- sort of a userland pipe, only we 10 * get more control. Not clear we gain anything, but it's less fragile 11 * than forking an external program. 12 */ 13 #include <pthread.h> 14 15 /* Getting data from a rolling buffer can be done via two interfaces. One 16 * interface (ringbuf_get) provides a pointer, while the other 17 * (ringbuf_get2) does not, and asks the ringbuf interface to provide a 18 * pointer. 19 * 20 * The area pointed to by the pointer returned from ringbuf_get2 is free 21 * to be read from by the calling thread until the next call to 22 * ringbuf_get. This implies in particular that we assume that there is 23 * exactly one threading getting data, no more. 24 * 25 * Note though that this is not implemented by means of a zero-copy 26 * mechanism (doing so would make rollback_put break the active area of 27 * data being read, in case a realloc() occurs). The data is copied to a 28 * buffer exclusively dedicated to reading. 29 * 30 * (the reading buffer is allocated only if needed, and freed by 31 * ringbuf_clear eventually) 32 */ 33 struct ringbuf_s { 34 char * p; 35 size_t alloc; 36 size_t avail_to_read; 37 size_t avail_to_write; 38 const char * rhead; 39 char * whead; 40 char * rbuf; /* Only for ringbuf_get2 */ 41 pthread_mutex_t mx[1]; 42 pthread_cond_t bored[1]; 43 int empty_count; 44 int full_count; 45 int done:1; 46 }; 47 48 typedef struct ringbuf_s ringbuf[1]; 49 typedef struct ringbuf_s * ringbuf_ptr; 50 typedef const struct ringbuf_s * ringbuf_srcptr; 51 52 #ifdef __cplusplus 53 extern "C" { 54 #endif 55 56 extern void ringbuf_init(ringbuf_ptr r, size_t initial_size); 57 extern void ringbuf_clear(ringbuf_ptr r); 58 extern int ringbuf_put(ringbuf_ptr r, char * p, size_t s); 59 extern void ringbuf_mark_done(ringbuf_ptr r); 60 extern int ringbuf_is_done(ringbuf_ptr r); 61 62 /* see above for the distinction between these two get() calls */ 63 extern int ringbuf_get(ringbuf_ptr r, char * p, size_t s); 64 extern int ringbuf_get2(ringbuf_ptr r, void ** p, size_t s); 65 66 extern int ringbuf_strchr(ringbuf_ptr r, int c, size_t offset); 67 68 /* Equivalent of doing ringbuf_put for all bytes from the stdio stream f. 69 * 70 * The only difference is that this call does not automatically enlarge 71 * the ring buffer, so an appropriate initial_size must have been 72 * provided on initialization 73 */ 74 extern ssize_t ringbuf_feed_stream(ringbuf_ptr r, FILE * f); 75 76 extern int ringbuf_skip_get(ringbuf_ptr r, size_t s); 77 78 /* A quick accessor macro which does a 1-byte fetch from the ring buffer. 79 * We must be sure that the get will succeed, and we must provide an 80 * auxiliary pointer (here s_) which will be updated by the macro. s_ has 81 * to be set to r_->rhead originally. 82 * 83 * n successive calls to RINGBUF_GET_ONE_BYTE must be followed by a call to 84 * ringbug_skip_get(r_, n) 85 */ 86 #define RINGBUF_GET_ONE_BYTE(c_, r_, s_) do { \ 87 c_ = *(s_)++; \ 88 if (s_ >= r_->p + r_->alloc) { \ 89 s_ = r_->p; \ 90 } \ 91 } while (0) 92 93 extern int ringbug_skip_get(ringbuf_ptr r, size_t s); 94 95 96 #ifdef __cplusplus 97 } 98 #endif 99 100 #endif /* ROLLBUF_H_ */ 101