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