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