-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathseqbuf.c
191 lines (141 loc) · 4.85 KB
/
seqbuf.c
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#define NETSTACK_LOG_UNIT "SEQBUF"
#include <netstack/log.h>
#include <netstack/col/seqbuf.h>
int seqbuf_init(seqbuf_t *buf, size_t start, size_t limit) {
if (buf == NULL)
return -EINVAL;
buf->head = NULL;
buf->tail = NULL;
buf->start = start;
buf->limit = limit;
buf->count = 0;
return 0;
}
void seqbuf_free(seqbuf_t *buf) {
if (buf != NULL) {
struct seqbuf_block *cur, *block = buf->head;
while (block != NULL) {
cur = block;
block = block->next;
free(cur);
}
buf->count = 0;
buf->start = 0;
}
}
long seqbuf_read(seqbuf_t *buf, size_t from, void *dest, size_t len) {
if (buf == NULL)
return -EINVAL;
long avail = seqbuf_available(buf, from);
if (avail <= 0)
return avail;
// We can only read at most what is available, or what is requested
size_t toread = MIN(len, avail);
// Offset by the initial byte
size_t blockofs = ((long) from - buf->start) % buf->limit;
// Cumulative sum of bytes read
size_t total_read = 0;
// Start at the first buffer and advance from there
struct seqbuf_block *block = buf->head;
// Advance blocks until the offset resides within the block
while (blockofs > block->len) {
blockofs -= block->len;
block = block->next;
// The next block doesn't exist (this shouldn't ever happen)
if (block == NULL) {
LOG(LERR, "buffer doesn't exist but it should.");
return -1;
}
}
// TODO: Try mmap() buffers into one contiguous region with one memcpy call
while (total_read < toread && block != NULL) {
void *data = block + 1;
size_t left_in_block = MIN(toread - total_read, block->len - blockofs);
// Read up to the end of the current buffer
memcpy(dest + total_read, data + blockofs, left_in_block);
// Advance counters and pointers
total_read += left_in_block;
// Offset within each buffer frame. mod bufsize prevents over-reading
blockofs = (blockofs + total_read) % block->len;
block = block->next;
}
return total_read;
}
long seqbuf_write(seqbuf_t *buf, const void *src, size_t len) {
if (buf == NULL)
return -EINVAL;
struct seqbuf_block *block;
// Allocate a new buffer to write the entire segment into
block = malloc(sizeof(struct seqbuf_block) + len);
if (block == NULL)
return -ENOMEM;
// Copy the data into it
void *data = block + 1;
memcpy(data, src, len);
// Update the buffer size
buf->count += len;
// There are no blocks in the buffer, this is the first
if (buf->head == NULL)
buf->head = block;
else
buf->tail->next = block;
block->len = len;
block->next = NULL;
// The tail of the buffer is always the newest block
buf->tail = block;
LOG(LINFO, "added new seqbuf block %p, %zu bytes, %zu total",
block, len, buf->count);
return len;
}
int seqbuf_consume(seqbuf_t *buf, size_t from, size_t len) {
if (buf == NULL)
return -EINVAL;
if (seqbuf_available(buf, from) < len) {
LOG(LERR, "len (%zu) > buf->count (%zu), from %zu", len, buf->count, from);
return -ERANGE;
}
LOG(LTRCE, "consuming %ld bytes from %zu (out of %ld)", len, from, buf->count);
size_t blocksize = buf->head->len - (from - buf->start);
while (len >= blocksize) {
// Update # of bytes left to remove
len -= blocksize;
// Move the start to the next unconsumed block
buf->start += blocksize;
buf->count -= blocksize;
// Store the old head so we can update the new one
void *tofree = buf->head;
// Update the head to the next block before free'ing the pointer
buf->head = buf->head->next;
free(tofree);
// We've free'd every block. We're done now
if (buf->head == NULL)
break;
// Update the blocksize for the next iteration if buf->head isn't NULL
blocksize = buf->head->len;
}
if (buf->head == NULL)
buf->tail = NULL;
return 0;
}
int seqbuf_consume_to(seqbuf_t *buf, size_t newstart) {
if (buf == NULL)
return -EINVAL;
long diff = ((long) newstart - buf->start) % buf->limit;
if (diff < 0) {
LOG(LNTCE, "consume_to(%zu) is %ld before current %zu",
newstart, -diff, buf->start);
return -ERANGE;
} if (diff <= 0)
return 0;
return seqbuf_consume(buf, buf->start, ((long) newstart - buf->start) % buf->limit);
}
long seqbuf_available(seqbuf_t *buf, size_t from) {
if (buf == NULL)
return -EINVAL;
// Only return >= 0 available bytes
return MAX(((long) (buf->start + buf->count) - from) % buf->limit, 0);
}