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->used = 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     return chdr->used;
83 }
84 
cbuf_free_space(struct cbufhead * chdr)85 size_t cbuf_free_space(struct cbufhead* chdr) {
86     return chdr->size - chdr->used;
87 }
88 
cbuf_size(struct cbufhead * chdr)89 size_t cbuf_size(struct cbufhead* chdr) {
90     return chdr->size;
91 }
92 
cbuf_empty(struct cbufhead * chdr)93 bool cbuf_empty(struct cbufhead* chdr) {
94     return chdr->used == 0;
95 }
96 
cbuf_get_w_index(const struct cbufhead * chdr)97 static inline size_t cbuf_get_w_index(const struct cbufhead* chdr) {
98     size_t until_end = chdr->size - chdr->r_index;
99     if (chdr->used < until_end) {
100         return chdr->r_index + chdr->used;
101     } else {
102         return chdr->used - until_end;
103     }
104 }
105 
cbuf_write(struct cbufhead * chdr,const void * data,size_t data_offset,size_t data_len,cbuf_copier_t copy_from)106 size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from) {
107     size_t free_space = cbuf_free_space(chdr);
108     uint8_t* buf_data;
109     size_t w_index;
110     size_t bytes_to_end;
111     if (free_space < data_len) {
112         data_len = free_space;
113     }
114     buf_data = chdr->buf;
115     w_index = cbuf_get_w_index(chdr);
116     bytes_to_end = chdr->size - w_index;
117     if (data_len <= bytes_to_end) {
118         copy_from(buf_data, w_index, data, data_offset, data_len);
119     } else {
120         copy_from(buf_data, w_index, data, data_offset, bytes_to_end);
121         copy_from(buf_data, 0, data, data_offset + bytes_to_end, data_len - bytes_to_end);
122     }
123     chdr->used += data_len;
124     return data_len;
125 }
126 
cbuf_read_unsafe(struct cbufhead * chdr,void * data,size_t data_offset,size_t numbytes,int pop,cbuf_copier_t copy_into)127 void cbuf_read_unsafe(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) {
128     uint8_t* buf_data = chdr->buf;
129     size_t bytes_to_end = chdr->size - chdr->r_index;
130     if (numbytes < bytes_to_end) {
131         copy_into(data, data_offset, buf_data, chdr->r_index, numbytes);
132         if (pop) {
133             chdr->r_index += numbytes;
134             chdr->used -= numbytes;
135         }
136     } else {
137         copy_into(data, data_offset, buf_data, chdr->r_index, bytes_to_end);
138         copy_into(data, data_offset + bytes_to_end, buf_data, 0, numbytes - bytes_to_end);
139         if (pop) {
140             chdr->r_index = numbytes - bytes_to_end;
141             chdr->used -= numbytes;
142         }
143     }
144 }
145 
cbuf_read(struct cbufhead * chdr,void * data,size_t data_offset,size_t numbytes,int pop,cbuf_copier_t copy_into)146 size_t cbuf_read(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) {
147     size_t used_space = cbuf_used_space(chdr);
148     if (used_space < numbytes) {
149         numbytes = used_space;
150     }
151     cbuf_read_unsafe(chdr, data, data_offset, numbytes, pop, copy_into);
152     return numbytes;
153 }
154 
cbuf_read_offset(struct cbufhead * chdr,void * data,size_t data_offset,size_t numbytes,size_t offset,cbuf_copier_t copy_into)155 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) {
156     size_t used_space = cbuf_used_space(chdr);
157     size_t oldpos;
158     if (used_space <= offset) {
159         return 0;
160     } else if (used_space < offset + numbytes) {
161         numbytes = used_space - offset;
162     }
163     oldpos = chdr->r_index;
164     chdr->r_index = (chdr->r_index + offset) % chdr->size;
165     cbuf_read_unsafe(chdr, data, data_offset, numbytes, 0, copy_into);
166     chdr->r_index = oldpos;
167     return numbytes;
168 }
169 
cbuf_pop(struct cbufhead * chdr,size_t numbytes)170 size_t cbuf_pop(struct cbufhead* chdr, size_t numbytes) {
171     size_t used_space = cbuf_used_space(chdr);
172     if (used_space < numbytes) {
173         numbytes = used_space;
174     }
175     chdr->r_index = (chdr->r_index + numbytes) % chdr->size;
176     chdr->used -= numbytes;
177     return numbytes;
178 }
179 
cbuf_swap(struct cbufhead * chdr,uint8_t * bitmap,size_t start_1,size_t start_2,size_t length)180 static void cbuf_swap(struct cbufhead* chdr, uint8_t* bitmap, size_t start_1, size_t start_2, size_t length) {
181     size_t i;
182 
183     /* Swap the data regions. */
184     for (i = 0; i != length; i++) {
185         uint8_t temp = chdr->buf[start_1 + i];
186         chdr->buf[start_1 + i] = chdr->buf[start_2 + i];
187         chdr->buf[start_2 + i] = temp;
188     }
189 
190     /* Swap the bitmaps. */
191     if (bitmap) {
192         bmp_swap(bitmap, start_1, start_2, length);
193     }
194 }
195 
cbuf_contiguify(struct cbufhead * chdr,uint8_t * bitmap)196 void cbuf_contiguify(struct cbufhead* chdr, uint8_t* bitmap) {
197     /*
198      * We treat contiguify as a special case of rotation. In principle, we
199      * could make this more efficient by inspecting R_INDEX, W_INDEX, and the
200      * bitmap to only move around in-sequence data and buffered out-of-sequence
201      * data, while ignoring the other bytes in the circular buffer. We leave
202      * this as an optimization to implement if/when it becomes necessary.
203      *
204      * The rotation algorithm is recursive. It is parameterized by three
205      * arguments. START_IDX is the index of the first element of the subarray
206      * that is being rotated. END_IDX is one plus the index of the last element
207      * of the subarray that is being rotated. MOVE_TO_START_IDX is the index of
208      * the element that should be located at START_IDX after the rotation.
209      *
210      * The algorithm is as follows. First, identify the largest block of data
211      * starting at MOVE_TO_START_IDX that can be swapped with data starting at
212      * START_IDX. If MOVE_TO_START_IDX is right at the midpoint of the array,
213      * then we're done. If it isn't, then we can treat the block of data that
214      * was just swapped to the beginning of the array as "done", and then
215      * complete the rotation by recursively rotating the rest of the array.
216      *
217      * Here's an example. Suppose that the array is "1 2 3 4 5 6 7 8 9" and
218      * MOVE_TO_START_IDX is the index of the element "3". First, we swap "1 2"
219      * AND "3 4" to get "3 4 1 2 5 6 7 8 9". Then, we recursively rotate the
220      * subarray "1 2 5 6 7 8 9", with MOVE_TO_START_IDX being the index of the
221      * element "5". The final array is "3 4 5 6 7 8 9 1 2".
222      *
223      * Here's another example. Suppose that the array is "1 2 3 4 5 6 7 8 9"
224      * and MOVE_TO_START_IDX is the index of the element "6". First, we swap
225      * "1 2 3 4" and "6 7 8 9" to get "6 7 8 9 5 1 2 3 4". Then, we recursively
226      * rotate the subarray "5 1 2 3 4", with MOVE_TO_START_IDX being the index
227      * of the element "1". The final array is "6 7 8 9 1 2 3 4 5".
228      *
229      * In order for this to work, it's important that the blocks that we
230      * choose are maximally large. If, in the first example, we swap only the
231      * elements "1" and "3", then the algorithm won't work. Note that "1 2" and
232      * "3 4" corresponds to maximally large blocks because if we make the
233      * blocks any bigger, they would overlap (e.g., "1 2 3" and "3 4 5"). In
234      * the second example, the block "6 7 8 9" is maximally large because we
235      * reach the end of the subarray.
236      *
237      * The algorithm above is tail-recursive (i.e., there's no more work to do
238      * after recursively rotating the subarray), so we write it as a while
239      * loop below. Each iteration of the while loop identifies the blocks to
240      * swap, swaps the blocks, and then sets up the indices such that the
241      * next iteration of the loop rotates the appropriate subarray.
242      *
243      * The performance of the algorithm is linear in the length of the array,
244      * with constant space overhead.
245      */
246     size_t start_idx = 0;
247     const size_t end_idx = chdr->size;
248     size_t move_to_start_idx = chdr->r_index;
249 
250     /* Invariant: start_idx <= move_to_start_idx <= end_idx */
251     while (start_idx < move_to_start_idx && move_to_start_idx < end_idx) {
252         size_t distance_from_start = move_to_start_idx - start_idx;
253         size_t distance_to_end = end_idx - move_to_start_idx;
254         if (distance_from_start <= distance_to_end) {
255             cbuf_swap(chdr, bitmap, start_idx, move_to_start_idx, distance_from_start);
256             start_idx = move_to_start_idx;
257             move_to_start_idx = move_to_start_idx + distance_from_start;
258         } else {
259             cbuf_swap(chdr, bitmap, start_idx, move_to_start_idx, distance_to_end);
260             start_idx = start_idx + distance_to_end;
261             // move_to_start_idx does not change
262         }
263     }
264 
265     /* Finally, fix up the indices. */
266     chdr->r_index = 0;
267 }
268 
cbuf_reference(const struct cbufhead * chdr,otLinkedBuffer * first,otLinkedBuffer * second)269 void cbuf_reference(const struct cbufhead* chdr, otLinkedBuffer* first, otLinkedBuffer* second) {
270     size_t until_end = chdr->size - chdr->r_index;
271     if (chdr->used <= until_end) {
272         first->mNext = NULL;
273         first->mData = &chdr->buf[chdr->r_index];
274         first->mLength = (uint16_t) chdr->used;
275     } else {
276         first->mNext = second;
277         first->mData = &chdr->buf[chdr->r_index];
278         first->mLength = (uint16_t) until_end;
279 
280         second->mNext = NULL;
281         second->mData = &chdr->buf[0];
282         second->mLength = (uint16_t) (chdr->used - until_end);
283     }
284 }
285 
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)286 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) {
287     uint8_t* buf_data = chdr->buf;
288     size_t free_space = cbuf_free_space(chdr);
289     size_t start_index;
290     size_t end_index;
291     size_t bytes_to_end;
292     if (offset > free_space) {
293         return 0;
294     } else if (offset + numbytes > free_space) {
295         numbytes = free_space - offset;
296     }
297     start_index = (cbuf_get_w_index(chdr) + offset) % chdr->size;
298     end_index = (start_index + numbytes) % chdr->size;
299     if (end_index >= start_index) {
300         copy_from(buf_data, start_index, data, data_offset, numbytes);
301         if (bitmap) {
302             bmp_setrange(bitmap, start_index, numbytes);
303         }
304     } else {
305         bytes_to_end = chdr->size - start_index;
306         copy_from(buf_data, start_index, data, data_offset, bytes_to_end);
307         copy_from(buf_data, 0, data, data_offset + bytes_to_end, numbytes - bytes_to_end);
308         if (bitmap) {
309             bmp_setrange(bitmap, start_index, bytes_to_end);
310             bmp_setrange(bitmap, 0, numbytes - bytes_to_end);
311         }
312     }
313     if (firstindex) {
314         *firstindex = start_index;
315     }
316     return numbytes;
317 }
318 
cbuf_reass_merge(struct cbufhead * chdr,size_t numbytes,uint8_t * bitmap)319 size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap) {
320     size_t old_w = cbuf_get_w_index(chdr);
321     size_t free_space = cbuf_free_space(chdr);
322     size_t bytes_to_end;
323     if (numbytes > free_space) {
324         numbytes = free_space;
325     }
326     if (bitmap) {
327         bytes_to_end = chdr->size - old_w;
328         if (numbytes <= bytes_to_end) {
329             bmp_clrrange(bitmap, old_w, numbytes);
330         } else {
331             bmp_clrrange(bitmap, old_w, bytes_to_end);
332             bmp_clrrange(bitmap, 0, numbytes - bytes_to_end);
333         }
334     }
335     chdr->used += numbytes;
336     return numbytes;
337 }
338 
cbuf_reass_count_set(struct cbufhead * chdr,size_t offset,uint8_t * bitmap,size_t limit)339 size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit) {
340     size_t bitmap_size = BITS_TO_BYTES(chdr->size);
341     size_t until_end;
342     offset = (cbuf_get_w_index(chdr) + offset) % chdr->size;
343     until_end = bmp_countset(bitmap, bitmap_size, offset, limit);
344     if (until_end >= limit || until_end < (chdr->size - offset)) {
345         // If we already hit the limit, or if the streak ended before wrapping, then stop here
346         return until_end;
347     }
348     limit -= until_end; // effectively, this is our limit when continuing
349     // 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)
350     return until_end + bmp_countset(bitmap, bitmap_size, 0, limit < offset ? limit : offset);
351 }
352 
cbuf_reass_within_offset(struct cbufhead * chdr,size_t offset,size_t index)353 int cbuf_reass_within_offset(struct cbufhead* chdr, size_t offset, size_t index) {
354     size_t range_start = cbuf_get_w_index(chdr);
355     size_t range_end = (range_start + offset) % chdr->size;
356     if (range_end >= range_start) {
357         return index >= range_start && index < range_end;
358     } else {
359         return index < range_end || (index >= range_start && index < chdr->size);
360     }
361 }
362