1
2 /** \file
3 * Safe and easy access to memory buffer.
4 */
5
6 #ifndef _USUAL_MBUF_H_
7 #define _USUAL_MBUF_H_
8
9 #include <usual/base.h>
10
11 #include <string.h>
12
13 /** MBuf structure. Allocated by user, can be in stack. */
14 struct MBuf {
15 uint8_t *data;
16 unsigned read_pos;
17 unsigned write_pos;
18 unsigned alloc_len;
19 bool reader;
20 bool fixed;
21 };
22
23 /** Format fragment for *printf() */
24 #define MBUF_FMT ".*s"
25 /** Argument layout for *printf() */
26 #define MBUF_ARG(m) ((m) ? mbuf_written(m) : 6), ((m) ? (const char *)mbuf_data(m) : "(null)")
27
28 /*
29 * Init functions
30 */
31
32 /** Initialize R/O buffer to fixed memory area. */
mbuf_init_fixed_reader(struct MBuf * buf,const void * ptr,unsigned len)33 static inline void mbuf_init_fixed_reader(struct MBuf *buf, const void *ptr, unsigned len)
34 {
35 buf->data = (uint8_t *)ptr;
36 buf->read_pos = 0;
37 buf->write_pos = len;
38 buf->alloc_len = len;
39 buf->reader = true;
40 buf->fixed = true;
41 }
42
43 /** Initialize R/W buffer to fixed memory area. */
mbuf_init_fixed_writer(struct MBuf * buf,void * ptr,unsigned len)44 static inline void mbuf_init_fixed_writer(struct MBuf *buf, void *ptr, unsigned len)
45 {
46 buf->data = (uint8_t *)ptr;
47 buf->read_pos = 0;
48 buf->write_pos = 0;
49 buf->alloc_len = len;
50 buf->reader = false;
51 buf->fixed = true;
52 }
53
54 /** Initialize R/W buffer to dynamically allocated memory area. */
mbuf_init_dynamic(struct MBuf * buf)55 static inline void mbuf_init_dynamic(struct MBuf *buf)
56 {
57 buf->data = NULL;
58 buf->read_pos = 0;
59 buf->write_pos = 0;
60 buf->alloc_len = 0;
61 buf->reader = false;
62 buf->fixed = false;
63 }
64
65 /** Free dynamically allocated area, if exists. */
mbuf_free(struct MBuf * buf)66 static inline void mbuf_free(struct MBuf *buf)
67 {
68 if (buf->data) {
69 if (!buf->fixed)
70 free(buf->data);
71 memset(buf, 0, sizeof(*buf));
72 }
73 }
74
75 /*
76 * Reset functions.
77 */
78
79 /** Move read cursor to start of buffer. */
mbuf_rewind_reader(struct MBuf * buf)80 static inline void mbuf_rewind_reader(struct MBuf *buf)
81 {
82 buf->read_pos = 0;
83 }
84
85 /** Move both read and write cursor to start of buffer. */
mbuf_rewind_writer(struct MBuf * buf)86 static inline void mbuf_rewind_writer(struct MBuf *buf)
87 {
88 if (!buf->reader) {
89 buf->read_pos = 0;
90 buf->write_pos = 0;
91 }
92 }
93
94 /*
95 * Info functions.
96 */
97
98 /** How many bytes can be read with read cursor. */
mbuf_avail_for_read(const struct MBuf * buf)99 static inline unsigned mbuf_avail_for_read(const struct MBuf *buf)
100 {
101 return buf->write_pos - buf->read_pos;
102 }
103
104 /** How many bytes can be written with write cursor, without realloc. */
mbuf_avail_for_write(const struct MBuf * buf)105 static inline unsigned mbuf_avail_for_write(const struct MBuf *buf)
106 {
107 if (!buf->reader && buf->alloc_len > buf->write_pos)
108 return buf->alloc_len - buf->write_pos;
109 return 0;
110 }
111
112 /** How many data bytes are in buffer. */
mbuf_written(const struct MBuf * buf)113 static inline unsigned mbuf_written(const struct MBuf *buf)
114 {
115 return buf->write_pos;
116 }
117
118 /** How many bytes have been read from buffer */
mbuf_consumed(const struct MBuf * buf)119 static inline unsigned mbuf_consumed(const struct MBuf *buf)
120 {
121 return buf->read_pos;
122 }
123
124 /** Return pointer to data area. */
mbuf_data(const struct MBuf * buf)125 static inline void *mbuf_data(const struct MBuf *buf)
126 {
127 return buf->data;
128 }
129
130 /** Do the mbufs contain same data. */
mbuf_eq(const struct MBuf * buf1,const struct MBuf * buf2)131 static inline bool mbuf_eq(const struct MBuf *buf1, const struct MBuf *buf2)
132 {
133 if (buf1 == buf2) return true;
134 if (!buf1 || !buf2 || (mbuf_written(buf1) != mbuf_written(buf2)))
135 return false;
136 return memcmp(mbuf_data(buf1), mbuf_data(buf2), mbuf_written(buf1)) == 0;
137 }
138
139 /** Complare mbuf to asciiz string */
mbuf_eq_str(const struct MBuf * buf1,const char * s)140 static inline bool mbuf_eq_str(const struct MBuf *buf1, const char *s)
141 {
142 struct MBuf tmp;
143 mbuf_init_fixed_reader(&tmp, s, strlen(s));
144 return mbuf_eq(buf1, &tmp);
145 }
146
147 /*
148 * Read functions.
149 */
150
151 /** Read a byte from read cursor. */
152 _MUSTCHECK
mbuf_get_byte(struct MBuf * buf,uint8_t * dst_p)153 static inline bool mbuf_get_byte(struct MBuf *buf, uint8_t *dst_p)
154 {
155 if (buf->read_pos + 1 > buf->write_pos)
156 return false;
157 *dst_p = buf->data[buf->read_pos++];
158 return true;
159 }
160
161 /** Read big-endian uint16 from read cursor. */
162 _MUSTCHECK
mbuf_get_char(struct MBuf * buf,char * dst_p)163 static inline bool mbuf_get_char(struct MBuf *buf, char *dst_p)
164 {
165 if (buf->read_pos + 1 > buf->write_pos)
166 return false;
167 *dst_p = buf->data[buf->read_pos++];
168 return true;
169 }
170
171 _MUSTCHECK
mbuf_get_uint16be(struct MBuf * buf,uint16_t * dst_p)172 static inline bool mbuf_get_uint16be(struct MBuf *buf, uint16_t *dst_p)
173 {
174 unsigned a, b;
175 if (buf->read_pos + 2 > buf->write_pos)
176 return false;
177 a = buf->data[buf->read_pos++];
178 b = buf->data[buf->read_pos++];
179 *dst_p = (a << 8) | b;
180 return true;
181 }
182
183 /** Read big-endian uint32 from read cursor. */
184 _MUSTCHECK
mbuf_get_uint32be(struct MBuf * buf,uint32_t * dst_p)185 static inline bool mbuf_get_uint32be(struct MBuf *buf, uint32_t *dst_p)
186 {
187 unsigned a, b, c, d;
188 if (buf->read_pos + 4 > buf->write_pos)
189 return false;
190 a = buf->data[buf->read_pos++];
191 b = buf->data[buf->read_pos++];
192 c = buf->data[buf->read_pos++];
193 d = buf->data[buf->read_pos++];
194 *dst_p = (a << 24) | (b << 16) | (c << 8) | d;
195 return true;
196 }
197
198 /** Get reference to len bytes from read cursor. */
199 _MUSTCHECK
mbuf_get_uint64be(struct MBuf * buf,uint64_t * dst_p)200 static inline bool mbuf_get_uint64be(struct MBuf *buf, uint64_t *dst_p)
201 {
202 uint32_t a, b;
203 if (!mbuf_get_uint32be(buf, &a)
204 || !mbuf_get_uint32be(buf, &b))
205 return false;
206 *dst_p = ((uint64_t)a << 32) | b;
207 return true;
208 }
209
210 _MUSTCHECK
mbuf_get_bytes(struct MBuf * buf,unsigned len,const uint8_t ** dst_p)211 static inline bool mbuf_get_bytes(struct MBuf *buf, unsigned len, const uint8_t **dst_p)
212 {
213 if (buf->read_pos + len > buf->write_pos)
214 return false;
215 *dst_p = buf->data + buf->read_pos;
216 buf->read_pos += len;
217 return true;
218 }
219
220 /** Get reference to asciiz string from read cursor. */
221 _MUSTCHECK
mbuf_get_chars(struct MBuf * buf,unsigned len,const char ** dst_p)222 static inline bool mbuf_get_chars(struct MBuf *buf, unsigned len, const char **dst_p)
223 {
224 if (buf->read_pos + len > buf->write_pos)
225 return false;
226 *dst_p = (char *)buf->data + buf->read_pos;
227 buf->read_pos += len;
228 return true;
229 }
230
231 _MUSTCHECK
mbuf_get_string(struct MBuf * buf,const char ** dst_p)232 static inline bool mbuf_get_string(struct MBuf *buf, const char **dst_p)
233 {
234 const char *res = (char *)buf->data + buf->read_pos;
235 const uint8_t *nul = memchr(res, 0, mbuf_avail_for_read(buf));
236 if (!nul)
237 return false;
238 *dst_p = res;
239 buf->read_pos = nul + 1 - buf->data;
240 return true;
241 }
242
243 /*
244 * Write functions.
245 */
246
247 /** Allocate more room if needed and the mbuf allows. */
248 _MUSTCHECK
249 bool mbuf_make_room(struct MBuf *buf, unsigned len);
250
251 /** Write a byte to write cursor. */
252 _MUSTCHECK
mbuf_write_byte(struct MBuf * buf,uint8_t val)253 static inline bool mbuf_write_byte(struct MBuf *buf, uint8_t val)
254 {
255 if (buf->write_pos + 1 > buf->alloc_len
256 && !mbuf_make_room(buf, 1))
257 return false;
258 buf->data[buf->write_pos++] = val;
259 return true;
260 }
261
262 /** Write len bytes to write cursor. */
263 _MUSTCHECK
mbuf_write(struct MBuf * buf,const void * ptr,unsigned len)264 static inline bool mbuf_write(struct MBuf *buf, const void *ptr, unsigned len)
265 {
266 if (buf->write_pos + len > buf->alloc_len
267 && !mbuf_make_room(buf, len))
268 return false;
269 if (len > 0)
270 memcpy(buf->data + buf->write_pos, ptr, len);
271 buf->write_pos += len;
272 return true;
273 }
274
275 /** writes full contents of another mbuf, without touching it */
276 _MUSTCHECK
mbuf_write_raw_mbuf(struct MBuf * dst,struct MBuf * src)277 static inline bool mbuf_write_raw_mbuf(struct MBuf *dst, struct MBuf *src)
278 {
279 return mbuf_write(dst, src->data, src->write_pos);
280 }
281
282 /** writes partial contents of another mbuf, with touching it */
283 _MUSTCHECK
mbuf_write_mbuf(struct MBuf * dst,struct MBuf * src,unsigned len)284 static inline bool mbuf_write_mbuf(struct MBuf *dst, struct MBuf *src, unsigned len)
285 {
286 const uint8_t *data;
287 if (!mbuf_get_bytes(src, len, &data))
288 return false;
289 if (!mbuf_write(dst, data, len)) {
290 src->read_pos -= len;
291 return false;
292 }
293 return true;
294 }
295
296 /** Fiil mbuf with byte value */
297 _MUSTCHECK
mbuf_fill(struct MBuf * buf,uint8_t byte,unsigned len)298 static inline bool mbuf_fill(struct MBuf *buf, uint8_t byte, unsigned len)
299 {
300 if (buf->write_pos + len > buf->alloc_len
301 && !mbuf_make_room(buf, len))
302 return false;
303 memset(buf->data + buf->write_pos, byte, len);
304 buf->write_pos += len;
305 return true;
306 }
307
308 /** remove some data from mbuf */
309 _MUSTCHECK
mbuf_cut(struct MBuf * buf,unsigned ofs,unsigned len)310 static inline bool mbuf_cut(struct MBuf *buf, unsigned ofs, unsigned len)
311 {
312 if (buf->reader)
313 return false;
314 if (ofs + len < buf->write_pos) {
315 unsigned endofs = ofs + len;
316 memmove(buf->data + ofs, buf->data + endofs, buf->write_pos - endofs);
317 buf->write_pos -= len;
318 } else if (ofs < buf->write_pos) {
319 buf->write_pos = ofs;
320 }
321 return true;
322 }
323
mbuf_copy(const struct MBuf * src,struct MBuf * dst)324 static inline void mbuf_copy(const struct MBuf *src, struct MBuf *dst)
325 {
326 *dst = *src;
327 }
328
329 _MUSTCHECK
mbuf_slice(struct MBuf * src,unsigned len,struct MBuf * dst)330 static inline bool mbuf_slice(struct MBuf *src, unsigned len, struct MBuf *dst)
331 {
332 if (len > mbuf_avail_for_read(src))
333 return false;
334 mbuf_init_fixed_reader(dst, src->data + src->read_pos, len);
335 src->read_pos += len;
336 return true;
337 }
338
339 #endif
340