Update Dasynq to 1.0.3.
[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[pos];
30     }
31     
32     char * get_buf_base()
33     {
34         return buf;
35     }
36     
37     int get_contiguous_length(char *ptr)
38     {
39         int eidx = cur_idx + length;
40         if (eidx >= SIZE) eidx -= SIZE;
41         
42         if (buf + eidx > ptr) {
43             return (buf + eidx) - ptr;
44         }
45         else {
46             return (buf + SIZE) - ptr;
47         }
48     }
49     
50     // fill by reading from the given fd, return positive if some was read or -1 on error.
51     int fill(int fd) noexcept
52     {
53         int pos = cur_idx + length;
54         if (pos >= SIZE) pos -= SIZE;
55         int max_count = std::min(SIZE - pos, SIZE - length);
56         ssize_t r = read(fd, buf + pos, max_count);
57         if (r >= 0) {
58             length += r;
59         }
60         return r;
61     }
62     
63     // fill by reading from the given fd, until at least the specified number of bytes are in
64     // the buffer. Return 0 if end-of-file reached before fill complete, or -1 on error.
65     int fill_to(int fd, int rlength) noexcept
66     {
67         while (length < rlength) {
68             int r = fill(fd);
69             if (r <= 0) return r;
70         }
71         return 1;
72     }
73     
74     // Trim the buffer to the specified length (must be less than current length)
75     void trim_to(int new_length)
76     {
77         length = new_length;
78     }
79     
80     char operator[](int idx) noexcept
81     {
82         int dest_idx = cur_idx + idx;
83         if (dest_idx > SIZE) dest_idx -= SIZE;
84         return buf[dest_idx];
85     }
86     
87     // Remove the given number of bytes from the start of the buffer.
88     void consume(int amount) noexcept
89     {
90         cur_idx += amount;
91         if (cur_idx >= SIZE) cur_idx -= SIZE;
92         length -= amount;
93     }
94     
95     // Extract bytes from the buffer. The bytes remain in the buffer.
96     void extract(char *dest, int index, int length) noexcept
97     {
98         index += cur_idx;
99         if (index >= SIZE) index -= SIZE;
100         if (index + length > SIZE) {
101             // wrap-around copy
102             int half = SIZE - index;
103             std::memcpy(dest, buf + index, half);
104             std::memcpy(dest + half, buf, length - half);
105         }
106         else {
107             std::memcpy(dest, buf + index, length);
108         }
109     }
110     
111     // Extract string of given length from given index
112     // Throws:  std::bad_alloc on allocation failure
113     std::string extract_string(int index, int length)
114     {
115         index += cur_idx;
116         if (index >= SIZE) index -= SIZE;
117         if (index + length > SIZE) {
118             std::string r(buf + index, SIZE - index);
119             r.insert(r.end(), buf, buf + length - (SIZE - index));
120             return r;
121         }
122         else {
123             return std::string(buf + index, length);
124         }
125     }
126     
127     // Append characters to the buffer. Caller must make certain there
128     // is enough space to contain the characters first.
129     void append(const char * s, int len) noexcept
130     {
131         int index = cur_idx + length;
132         if (index >= SIZE) index -= SIZE;
133
134         length += len; // (before we destroy len)
135         
136         int max = SIZE - index;
137         std::memcpy(buf + index, s, std::min(max, len));
138         if (len > max) {
139             // Wrapped around buffer: copy the rest
140             s += max;
141             len -= max;
142             std::memcpy(buf, s, len);
143         }
144     }
145 };
146
147 #endif