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