1 /*
2 * Copyright (c) 2018, Sam Kumar <samkumar@cs.berkeley.edu>
3 * Copyright (c) 2018, University of California, Berkeley
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /* CIRCULAR BUFFER */
31 #include "cbuf.h"
32 #include "bitmap.h"
33
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38
39 #include <openthread/message.h>
40 #include <openthread/tcp.h>
41
42 /*
43 * Copiers for copying from/into cbufs into/from arrays or OpenThraed messages
44 */
45
cbuf_copy_into_buffer(void * buffer,size_t buffer_offset,const void * arr,size_t arr_offset,size_t num_bytes)46 void cbuf_copy_into_buffer(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes) {
47 uint8_t* bufptr = (uint8_t*) buffer;
48 const uint8_t* arrptr = (const uint8_t*) arr;
49 memcpy(bufptr + buffer_offset, arrptr + arr_offset, num_bytes);
50 }
51
cbuf_copy_from_buffer(void * arr,size_t arr_offset,const void * buffer,size_t buffer_offset,size_t num_bytes)52 void cbuf_copy_from_buffer(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes) {
53 uint8_t* arrptr = (uint8_t*) arr;
54 const uint8_t* bufptr = (const uint8_t*) buffer;
55 memcpy(arrptr + arr_offset, bufptr + buffer_offset, num_bytes);
56 }
57
cbuf_copy_into_message(void * buffer,size_t buffer_offset,const void * arr,size_t arr_offset,size_t num_bytes)58 void cbuf_copy_into_message(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes) {
59 otMessage* message = (otMessage*) buffer;
60 uint8_t* arrptr = (uint8_t*) arr;
61 otMessageWrite(message, (uint16_t) buffer_offset, arrptr + arr_offset, (uint16_t) num_bytes);
62 }
63
cbuf_copy_from_message(void * arr,size_t arr_offset,const void * buffer,size_t buffer_offset,size_t num_bytes)64 void cbuf_copy_from_message(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes) {
65 uint8_t* arrptr = (uint8_t*) arr;
66 const otMessage* message = (const otMessage*) buffer;
67 otMessageRead(message, (uint16_t) buffer_offset, arrptr + arr_offset, (uint16_t) num_bytes);
68 }
69
70 /*
71 * Cbuf implementation.
72 */
73
cbuf_init(struct cbufhead * chdr,uint8_t * buf,size_t len)74 void cbuf_init(struct cbufhead* chdr, uint8_t* buf, size_t len) {
75 chdr->r_index = 0;
76 chdr->w_index = 0;
77 chdr->size = len;
78 chdr->buf = buf;
79 }
80
cbuf_used_space(struct cbufhead * chdr)81 size_t cbuf_used_space(struct cbufhead* chdr) {
82 if (chdr->w_index >= chdr->r_index) {
83 return chdr->w_index - chdr->r_index;
84 } else {
85 return chdr->size + chdr->w_index - chdr->r_index;
86 }
87 }
88
89 /* There's always one byte of lost space so I can distinguish between a full
90 buffer and an empty buffer. */
cbuf_free_space(struct cbufhead * chdr)91 size_t cbuf_free_space(struct cbufhead* chdr) {
92 return chdr->size - 1 - cbuf_used_space(chdr);
93 }
94
cbuf_size(struct cbufhead * chdr)95 size_t cbuf_size(struct cbufhead* chdr) {
96 return chdr->size - 1;
97 }
98
cbuf_empty(struct cbufhead * chdr)99 bool cbuf_empty(struct cbufhead* chdr) {
100 return (chdr->w_index == chdr->r_index);
101 }
102
cbuf_write(struct cbufhead * chdr,const void * data,size_t data_offset,size_t data_len,cbuf_copier_t copy_from)103 size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from) {
104 size_t free_space = cbuf_free_space(chdr);
105 uint8_t* buf_data;
106 size_t fw_index;
107 size_t bytes_to_end;
108 if (free_space < data_len) {
109 data_len = free_space;
110 }
111 buf_data = chdr->buf;
112 fw_index = (chdr->w_index + data_len) % chdr->size;
113 if (fw_index >= chdr->w_index) {
114 copy_from(buf_data, chdr->w_index, data, data_offset, data_len);
115 } else {
116 bytes_to_end = chdr->size - chdr->w_index;
117 copy_from(buf_data, chdr->w_index, data, data_offset, bytes_to_end);
118 copy_from(buf_data, 0, data, data_offset + bytes_to_end, data_len - bytes_to_end);
119 }
120 chdr->w_index = fw_index;
121 return data_len;
122 }
123
cbuf_read_unsafe(struct cbufhead * chdr,void * data,size_t data_offset,size_t numbytes,int pop,cbuf_copier_t copy_into)124 void cbuf_read_unsafe(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) {
125 uint8_t* buf_data = chdr->buf;
126 size_t fr_index = (chdr->r_index + numbytes) % chdr->size;
127 size_t bytes_to_end;
128 if (fr_index >= chdr->r_index) {
129 copy_into(data, data_offset, buf_data, chdr->r_index, numbytes);
130 } else {
131 bytes_to_end = chdr->size - chdr->r_index;
132 copy_into(data, data_offset, buf_data, chdr->r_index, bytes_to_end);
133 copy_into(data, data_offset + bytes_to_end, buf_data, 0, numbytes - bytes_to_end);
134 }
135 if (pop) {
136 chdr->r_index = fr_index;
137 }
138 }
139
cbuf_read(struct cbufhead * chdr,void * data,size_t data_offset,size_t numbytes,int pop,cbuf_copier_t copy_into)140 size_t cbuf_read(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) {
141 size_t used_space = cbuf_used_space(chdr);
142 if (used_space < numbytes) {
143 numbytes = used_space;
144 }
145 cbuf_read_unsafe(chdr, data, data_offset, numbytes, pop, copy_into);
146 return numbytes;
147 }
148
cbuf_read_offset(struct cbufhead * chdr,void * data,size_t data_offset,size_t numbytes,size_t offset,cbuf_copier_t copy_into)149 size_t cbuf_read_offset(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, size_t offset, cbuf_copier_t copy_into) {
150 size_t used_space = cbuf_used_space(chdr);
151 size_t oldpos;
152 if (used_space <= offset) {
153 return 0;
154 } else if (used_space < offset + numbytes) {
155 numbytes = used_space - offset;
156 }
157 oldpos = chdr->r_index;
158 chdr->r_index = (chdr->r_index + offset) % chdr->size;
159 cbuf_read_unsafe(chdr, data, data_offset, numbytes, 0, copy_into);
160 chdr->r_index = oldpos;
161 return numbytes;
162 }
163
cbuf_pop(struct cbufhead * chdr,size_t numbytes)164 size_t cbuf_pop(struct cbufhead* chdr, size_t numbytes) {
165 size_t used_space = cbuf_used_space(chdr);
166 if (used_space < numbytes) {
167 numbytes = used_space;
168 }
169 chdr->r_index = (chdr->r_index + numbytes) % chdr->size;
170 return numbytes;
171 }
172
cbuf_reference(const struct cbufhead * chdr,otLinkedBuffer * first,otLinkedBuffer * second)173 void cbuf_reference(const struct cbufhead* chdr, otLinkedBuffer* first, otLinkedBuffer* second) {
174 if (chdr->w_index >= chdr->r_index) {
175 first->mNext = NULL;
176 first->mData = &chdr->buf[chdr->r_index];
177 first->mLength = (uint16_t) (chdr->w_index - chdr->r_index);
178 } else {
179 first->mNext = second;
180 first->mData = &chdr->buf[chdr->r_index];
181 first->mLength = (uint16_t) (chdr->size - chdr->r_index);
182
183 second->mNext = NULL;
184 second->mData = &chdr->buf[0];
185 second->mLength = (uint16_t) chdr->w_index;
186 }
187 }
188
cbuf_reass_write(struct cbufhead * chdr,size_t offset,const void * data,size_t data_offset,size_t numbytes,uint8_t * bitmap,size_t * firstindex,cbuf_copier_t copy_from)189 size_t cbuf_reass_write(struct cbufhead* chdr, size_t offset, const void* data, size_t data_offset, size_t numbytes, uint8_t* bitmap, size_t* firstindex, cbuf_copier_t copy_from) {
190 uint8_t* buf_data = chdr->buf;
191 size_t free_space = cbuf_free_space(chdr);
192 size_t start_index;
193 size_t end_index;
194 size_t bytes_to_end;
195 if (offset > free_space) {
196 return 0;
197 } else if (offset + numbytes > free_space) {
198 numbytes = free_space - offset;
199 }
200 start_index = (chdr->w_index + offset) % chdr->size;
201 end_index = (start_index + numbytes) % chdr->size;
202 if (end_index >= start_index) {
203 copy_from(buf_data, start_index, data, data_offset, numbytes);
204 if (bitmap) {
205 bmp_setrange(bitmap, start_index, numbytes);
206 }
207 } else {
208 bytes_to_end = chdr->size - start_index;
209 copy_from(buf_data, start_index, data, data_offset, bytes_to_end);
210 copy_from(buf_data, 0, data, data_offset + bytes_to_end, numbytes - bytes_to_end);
211 if (bitmap) {
212 bmp_setrange(bitmap, start_index, bytes_to_end);
213 bmp_setrange(bitmap, 0, numbytes - bytes_to_end);
214 }
215 }
216 if (firstindex) {
217 *firstindex = start_index;
218 }
219 return numbytes;
220 }
221
cbuf_reass_merge(struct cbufhead * chdr,size_t numbytes,uint8_t * bitmap)222 size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap) {
223 size_t old_w = chdr->w_index;
224 size_t free_space = cbuf_free_space(chdr);
225 size_t bytes_to_end;
226 if (numbytes > free_space) {
227 numbytes = free_space;
228 }
229 chdr->w_index = (chdr->w_index + numbytes) % chdr->size;
230 if (bitmap) {
231 if (chdr->w_index >= old_w) {
232 bmp_clrrange(bitmap, old_w, numbytes);
233 } else {
234 bytes_to_end = chdr->size - old_w;
235 bmp_clrrange(bitmap, old_w, bytes_to_end);
236 bmp_clrrange(bitmap, 0, numbytes - bytes_to_end);
237 }
238 }
239 return numbytes;
240 }
241
cbuf_reass_count_set(struct cbufhead * chdr,size_t offset,uint8_t * bitmap,size_t limit)242 size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit) {
243 size_t bitmap_size = BITS_TO_BYTES(chdr->size);
244 size_t until_end;
245 offset = (chdr->w_index + offset) % chdr->size;
246 until_end = bmp_countset(bitmap, bitmap_size, offset, limit);
247 if (until_end >= limit || until_end < (chdr->size - offset)) {
248 // If we already hit the limit, or if the streak ended before wrapping, then stop here
249 return until_end;
250 }
251 limit -= until_end; // effectively, this is our limit when continuing
252 // Continue until either the new limit or until we have scanned OFFSET bits (if we scan more than OFFSET bits, we'll wrap and scan some parts twice)
253 return until_end + bmp_countset(bitmap, bitmap_size, 0, limit < offset ? limit : offset);
254 }
255
cbuf_reass_within_offset(struct cbufhead * chdr,size_t offset,size_t index)256 int cbuf_reass_within_offset(struct cbufhead* chdr, size_t offset, size_t index) {
257 size_t range_start = chdr->w_index;
258 size_t range_end = (range_start + offset) % chdr->size;
259 if (range_end >= range_start) {
260 return index >= range_start && index < range_end;
261 } else {
262 return index < range_end || (index >= range_start && index < chdr->size);
263 }
264 }
265