1c05174a6ea8db0f1be1519a0a14cb8702fe9a40
[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     // Trim the buffer to the specified length (must be less than current length)
70     void trim_to(int new_length)
71     {
72         length = new_length;
73     }
74     
75     char operator[](int idx) noexcept
76     {
77         int dest_idx = cur_idx + idx;
78         if (dest_idx > SIZE) dest_idx -= SIZE;
79         return buf[dest_idx];
80     }
81     
82     // Remove the given number of bytes from the start of the buffer.
83     void consume(int amount) noexcept
84     {
85         cur_idx += amount;
86         if (cur_idx >= SIZE) cur_idx -= SIZE;
87         length -= amount;
88     }
89     
90     // Extract bytes from the buffer. The bytes remain in the buffer.
91     void extract(char *dest, int index, int length) noexcept
92     {
93         index += cur_idx;
94         if (index >= SIZE) index -= SIZE;
95         if (index + length > SIZE) {
96             // wrap-around copy
97             int half = SIZE - index;
98             std::memcpy(dest, buf + index, half);
99             std::memcpy(dest + half, buf, length - half);
100         }
101         else {
102             std::memcpy(dest, buf + index, length);
103         }
104     }
105     
106     // Extract string of given length from given index
107     // Throws:  std::bad_alloc on allocation failure
108     std::string extract_string(int index, int length)
109     {
110         index += cur_idx;
111         if (index >= SIZE) index -= SIZE;
112         if (index + length > SIZE) {
113             std::string r(buf + index, SIZE - index);
114             r.insert(r.end(), buf, buf + length - (SIZE - index));
115             return r;
116         }
117         else {
118             return std::string(buf + index, length);
119         }
120     }
121     
122     // Append characters to the buffer. Caller must make certain there
123     // is enough space to contain the characters first.
124     void append(const char * s, int len) noexcept
125     {
126         int index = cur_idx + length;
127         if (index >= SIZE) index -= SIZE;
128
129         length += len; // (before we destroy len)
130         
131         int max = SIZE - index;
132         std::memcpy(buf + index, s, std::min(max, len));
133         if (len > max) {
134             // Wrapped around buffer: copy the rest
135             s += max;
136             len -= max;
137             std::memcpy(buf, s, len);
138         }
139     }
140 };
141
142 #endif