978720430b47a0b51d8a8e11eb75b35c139fba04
[oweals/dinit.git] / src / cpbuffer.h
1 #ifndef CPBUFFER_H
2 #define CPBUFFER_H
3
4 #include <cstring>
5
6 // control protocol buffer, a circular buffer with 1024-byte capacity.
7 template <int SIZE> class CPBuffer
8 {
9     char buf[SIZE];
10     int cur_idx = 0;
11     int length = 0;  // number of elements in the buffer
12     
13     public:
14     int get_length() noexcept
15     {
16         return length;
17     }
18     
19     int get_free() noexcept
20     {
21         return SIZE - length;
22     }
23     
24     char * get_ptr(int index)
25     {
26         int pos = cur_idx + index;
27         if (pos >= SIZE) pos -= SIZE;
28     
29         return &buf[cur_idx];
30     }
31     
32     int get_contiguous_length(char *ptr)
33     {
34         int eidx = cur_idx + length;
35         if (eidx >= SIZE) eidx -= SIZE;
36         
37         if (buf + eidx > ptr) {
38             return (buf + eidx) - ptr;
39         }
40         else {
41             return (buf + SIZE) - ptr;
42         }
43     }
44     
45     // fill by reading from the given fd, return positive if some was read or -1 on error.
46     int fill(int fd) noexcept
47     {
48         int pos = cur_idx + length;
49         if (pos >= SIZE) pos -= SIZE;
50         int max_count = std::min(SIZE - pos, SIZE - length);
51         ssize_t r = read(fd, buf + pos, max_count);
52         if (r >= 0) {
53             length += r;
54         }
55         return r;
56     }
57     
58     // fill by reading from the given fd, until at least the specified number of bytes are in
59     // the buffer. Return 0 if end-of-file reached before fill complete, or -1 on error.
60     int fill_to(int fd, int rlength) noexcept
61     {
62         while (length < rlength) {
63             int r = fill(fd);
64             if (r <= 0) return r;
65         }
66         return 1;
67     }
68     
69     char operator[](int idx) noexcept
70     {
71         int dest_idx = cur_idx + idx;
72         if (dest_idx > SIZE) dest_idx -= SIZE;
73         return buf[dest_idx];
74     }
75     
76     // Remove the given number of bytes from the start of the buffer.
77     void consume(int amount) noexcept
78     {
79         cur_idx += amount;
80         if (cur_idx >= SIZE) cur_idx -= SIZE;
81         length -= amount;
82     }
83     
84     // Extract bytes from the buffer. The bytes remain in the buffer.
85     void extract(char *dest, int index, int length) noexcept
86     {
87         index += cur_idx;
88         if (index >= SIZE) index -= SIZE;
89         if (index + length > SIZE) {
90             // wrap-around copy
91             int half = SIZE - index;
92             std::memcpy(dest, buf + index, half);
93             std::memcpy(dest + half, buf, length - half);
94         }
95         else {
96             std::memcpy(dest, buf + index, length);
97         }
98     }
99     
100     // Extract string of given length from given index
101     // Throws:  std::bad_alloc on allocation failure
102     std::string extract_string(int index, int length)
103     {
104         index += cur_idx;
105         if (index >= SIZE) index -= SIZE;
106         if (index + length > SIZE) {
107             std::string r(buf + index, SIZE - index);
108             r.insert(r.end(), buf, buf + length - (SIZE - index));
109             return r;
110         }
111         else {
112             return std::string(buf + index, length);
113         }
114     }
115     
116     // Append characters to the buffer. Caller must make certain there
117     // is enough space to contain the characters first.
118     void append(const char * s, int len) noexcept
119     {
120         int index = cur_idx + length;
121         if (index >= SIZE) index -= SIZE;
122
123         length += len; // (before we destroy len)
124         
125         int max = SIZE - index;
126         std::memcpy(buf + index, s, std::min(max, len));
127         if (len > max) {
128             // Wrapped around buffer: copy the rest
129             s += max;
130             len -= max;
131             std::memcpy(buf, s, len);
132         }
133     }
134 };
135
136 #endif