1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | #include <assert.h> |
5 | #include <stddef.h> |
6 | #include <string.h> |
7 | |
8 | #include "nvim/memory.h" |
9 | #include "nvim/vim.h" |
10 | #include "nvim/rbuffer.h" |
11 | |
12 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
13 | # include "rbuffer.c.generated.h" |
14 | #endif |
15 | |
16 | /// Creates a new `RBuffer` instance. |
17 | RBuffer *rbuffer_new(size_t capacity) |
18 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET |
19 | { |
20 | if (!capacity) { |
21 | capacity = 0x10000; |
22 | } |
23 | |
24 | RBuffer *rv = xcalloc(1, sizeof(RBuffer) + capacity); |
25 | rv->full_cb = rv->nonfull_cb = NULL; |
26 | rv->data = NULL; |
27 | rv->size = 0; |
28 | rv->write_ptr = rv->read_ptr = rv->start_ptr; |
29 | rv->end_ptr = rv->start_ptr + capacity; |
30 | rv->temp = NULL; |
31 | return rv; |
32 | } |
33 | |
34 | void rbuffer_free(RBuffer *buf) |
35 | { |
36 | xfree(buf->temp); |
37 | xfree(buf); |
38 | } |
39 | |
40 | size_t rbuffer_size(RBuffer *buf) FUNC_ATTR_NONNULL_ALL |
41 | { |
42 | return buf->size; |
43 | } |
44 | |
45 | size_t rbuffer_capacity(RBuffer *buf) FUNC_ATTR_NONNULL_ALL |
46 | { |
47 | return (size_t)(buf->end_ptr - buf->start_ptr); |
48 | } |
49 | |
50 | size_t rbuffer_space(RBuffer *buf) FUNC_ATTR_NONNULL_ALL |
51 | { |
52 | return rbuffer_capacity(buf) - buf->size; |
53 | } |
54 | |
55 | /// Return a pointer to a raw buffer containing the first empty slot available |
56 | /// for writing. The second argument is a pointer to the maximum number of |
57 | /// bytes that could be written. |
58 | /// |
59 | /// It is necessary to call this function twice to ensure all empty space was |
60 | /// used. See RBUFFER_UNTIL_FULL for a macro that simplifies this task. |
61 | char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL |
62 | { |
63 | if (buf->size == rbuffer_capacity(buf)) { |
64 | *write_count = 0; |
65 | return NULL; |
66 | } |
67 | |
68 | if (buf->write_ptr >= buf->read_ptr) { |
69 | *write_count = (size_t)(buf->end_ptr - buf->write_ptr); |
70 | } else { |
71 | *write_count = (size_t)(buf->read_ptr - buf->write_ptr); |
72 | } |
73 | |
74 | return buf->write_ptr; |
75 | } |
76 | |
77 | // Reset an RBuffer so read_ptr is at the beginning of the memory. If |
78 | // necessary, this moves existing data by allocating temporary memory. |
79 | void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL |
80 | { |
81 | size_t temp_size; |
82 | if ((temp_size = rbuffer_size(buf))) { |
83 | if (buf->temp == NULL) { |
84 | buf->temp = xcalloc(1, rbuffer_capacity(buf)); |
85 | } |
86 | rbuffer_read(buf, buf->temp, buf->size); |
87 | } |
88 | buf->read_ptr = buf->write_ptr = buf->start_ptr; |
89 | if (temp_size) { |
90 | rbuffer_write(buf, buf->temp, temp_size); |
91 | } |
92 | } |
93 | |
94 | /// Adjust `rbuffer` write pointer to reflect produced data. This is called |
95 | /// automatically by `rbuffer_write`, but when using `rbuffer_write_ptr` |
96 | /// directly, this needs to called after the data was copied to the internal |
97 | /// buffer. The write pointer will be wrapped if required. |
98 | void rbuffer_produced(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL |
99 | { |
100 | assert(count && count <= rbuffer_space(buf)); |
101 | |
102 | buf->write_ptr += count; |
103 | if (buf->write_ptr >= buf->end_ptr) { |
104 | // wrap around |
105 | buf->write_ptr -= rbuffer_capacity(buf); |
106 | } |
107 | |
108 | buf->size += count; |
109 | if (buf->full_cb && !rbuffer_space(buf)) { |
110 | buf->full_cb(buf, buf->data); |
111 | } |
112 | } |
113 | |
114 | /// Return a pointer to a raw buffer containing the first byte available |
115 | /// for reading. The second argument is a pointer to the maximum number of |
116 | /// bytes that could be read. |
117 | /// |
118 | /// It is necessary to call this function twice to ensure all available bytes |
119 | /// were read. See RBUFFER_UNTIL_EMPTY for a macro that simplifies this task. |
120 | char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL |
121 | { |
122 | if (!buf->size) { |
123 | *read_count = 0; |
124 | return buf->read_ptr; |
125 | } |
126 | |
127 | if (buf->read_ptr < buf->write_ptr) { |
128 | *read_count = (size_t)(buf->write_ptr - buf->read_ptr); |
129 | } else { |
130 | *read_count = (size_t)(buf->end_ptr - buf->read_ptr); |
131 | } |
132 | |
133 | return buf->read_ptr; |
134 | } |
135 | |
136 | /// Adjust `rbuffer` read pointer to reflect consumed data. This is called |
137 | /// automatically by `rbuffer_read`, but when using `rbuffer_read_ptr` |
138 | /// directly, this needs to called after the data was copied from the internal |
139 | /// buffer. The read pointer will be wrapped if required. |
140 | void rbuffer_consumed(RBuffer *buf, size_t count) |
141 | FUNC_ATTR_NONNULL_ALL |
142 | { |
143 | assert(count && count <= buf->size); |
144 | |
145 | buf->read_ptr += count; |
146 | if (buf->read_ptr >= buf->end_ptr) { |
147 | buf->read_ptr -= rbuffer_capacity(buf); |
148 | } |
149 | |
150 | bool was_full = buf->size == rbuffer_capacity(buf); |
151 | buf->size -= count; |
152 | if (buf->nonfull_cb && was_full) { |
153 | buf->nonfull_cb(buf, buf->data); |
154 | } |
155 | } |
156 | |
157 | // Higher level functions for copying from/to RBuffer instances and data |
158 | // pointers |
159 | size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size) |
160 | FUNC_ATTR_NONNULL_ALL |
161 | { |
162 | size_t size = src_size; |
163 | |
164 | RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { |
165 | size_t copy_count = MIN(src_size, wcnt); |
166 | memcpy(wptr, src, copy_count); |
167 | rbuffer_produced(buf, copy_count); |
168 | |
169 | if (!(src_size -= copy_count)) { |
170 | return size; |
171 | } |
172 | |
173 | src += copy_count; |
174 | } |
175 | |
176 | return size - src_size; |
177 | } |
178 | |
179 | size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size) |
180 | FUNC_ATTR_NONNULL_ALL |
181 | { |
182 | size_t size = dst_size; |
183 | |
184 | RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) { |
185 | size_t copy_count = MIN(dst_size, rcnt); |
186 | memcpy(dst, rptr, copy_count); |
187 | rbuffer_consumed(buf, copy_count); |
188 | |
189 | if (!(dst_size -= copy_count)) { |
190 | return size; |
191 | } |
192 | |
193 | dst += copy_count; |
194 | } |
195 | |
196 | return size - dst_size; |
197 | } |
198 | |
199 | char *rbuffer_get(RBuffer *buf, size_t index) |
200 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET |
201 | { |
202 | assert(index < buf->size); |
203 | char *rptr = buf->read_ptr + index; |
204 | if (rptr >= buf->end_ptr) { |
205 | rptr -= rbuffer_capacity(buf); |
206 | } |
207 | return rptr; |
208 | } |
209 | |
210 | int rbuffer_cmp(RBuffer *buf, const char *str, size_t count) |
211 | FUNC_ATTR_NONNULL_ALL |
212 | { |
213 | assert(count <= buf->size); |
214 | size_t rcnt; |
215 | (void)rbuffer_read_ptr(buf, &rcnt); |
216 | size_t n = MIN(count, rcnt); |
217 | int rv = memcmp(str, buf->read_ptr, n); |
218 | count -= n; |
219 | size_t remaining = buf->size - rcnt; |
220 | |
221 | if (rv || !count || !remaining) { |
222 | return rv; |
223 | } |
224 | |
225 | return memcmp(str + n, buf->start_ptr, count); |
226 | } |
227 | |
228 | |