summaryrefslogtreecommitdiff
path: root/kern/cbuf.h
blob: 726435d1b348205be15e13b1bcf579135b40e4dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Copyright (c) 2015-2017 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Circular character buffer.
 */

#ifndef _KERN_CBUF_H
#define _KERN_CBUF_H

#include <stdbool.h>
#include <stddef.h>

/*
 * Circular buffer descriptor.
 *
 * The buffer capacity must be a power-of-two. Indexes are absolute values
 * which can overflow. Their difference cannot exceed the capacity.
 */
struct cbuf {
    char *buf;
    size_t capacity;
    size_t start;
    size_t end;
};

static inline size_t
cbuf_capacity(const struct cbuf *cbuf)
{
    return cbuf->capacity;
}

static inline size_t
cbuf_start(const struct cbuf *cbuf)
{
    return cbuf->start;
}

static inline size_t
cbuf_end(const struct cbuf *cbuf)
{
    return cbuf->end;
}

static inline size_t
cbuf_size(const struct cbuf *cbuf)
{
    return cbuf->end - cbuf->start;
}

static inline void
cbuf_clear(struct cbuf *cbuf)
{
    cbuf->start = cbuf->end;
}

static inline bool
cbuf_range_valid(const struct cbuf *cbuf, size_t start, size_t end)
{
    return (((end - start) <= cbuf_size(cbuf))
            && ((start - cbuf->start) <= cbuf_size(cbuf))
            && ((cbuf->end - end) <= cbuf_size(cbuf)));
}

/*
 * Initialize a circular buffer.
 *
 * The descriptor is set to use the given buffer for storage. Capacity
 * must be a power-of-two.
 */
void cbuf_init(struct cbuf *cbuf, char *buf, size_t capacity);

/*
 * Append a buffer to a circular buffer.
 *
 * If erasing old data is not allowed, and the circular buffer doesn't have
 * enough unused bytes for the new data, ERROR_AGAIN is returned. Otherwise,
 * the end index is increased by the new data size, possibly erasing old
 * data, in which case, the start index is updated accordingly.
 */
int cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase);

/*
 * Read bytes from a circular buffer.
 *
 * If the buffer is empty, ERROR_AGAIN is returned. Otherwise, the oldest
 * bytes are stored into the given buffer. On entry, the sizep argument points
 * to the size of the given buffer. On exit, that value is updated to the
 * number of bytes actually stored. If successful, the start index is increased
 * by the amount of bytes read.
 */
int cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep);

/*
 * Append a byte to a circular buffer.
 *
 * If erasing old data is not allowed, and the circular buffer is full,
 * ERROR_AGAIN is returned. Otherwise, the end index is incremented and, if the
 * buffer is full, the oldest byte is overwritten and the start index
 * is updated accordingly.
 */
int cbuf_pushb(struct cbuf *cbuf, char byte, bool erase);

/*
 * Read a byte from a circular buffer.
 *
 * If the buffer is empty, ERROR_AGAIN is returned. Otherwise, the oldest
 * byte is stored at the bytep address, the start index is incremented,
 * and 0 is returned.
 */
int cbuf_popb(struct cbuf *cbuf, char *bytep);

/*
 * Write into a circular buffer at a specific location.
 *
 * If the given index is outside buffer boundaries, ERROR_INVAL is returned.
 * Otherwise size bytes are copied into the circular buffer. If the range
 * in the circular buffer goes beyond its end, the end index is updated as
 * appropriate. If the buffer is full when extending its end, the oldest
 * bytes are overwritten and the start index is updated accordingly.
 */
int cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size);

/*
 * Read from a circular buffer at a specific location.
 *
 * If the given index is outside buffer boundaries, ERROR_INVAL is returned.
 * Otherwise at most *sizep bytes are copied into the given byte buffer,
 * and *sizep is updated to the number of bytes actually copied.
 *
 * The circular buffer isn't changed by this operation.
 */
int cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep);

#endif /* _KERN_CBUF_H */