From 5564ea05304404e0c83a0de06adea672b305e31d Mon Sep 17 00:00:00 2001 From: Sam Kumar Date: Fri, 24 Sep 2021 23:15:40 -0700 Subject: [PATCH] [tcp] add TCPlp data buffering code (used instead of FreeBSD's data buffering) (#6926) This commit the libraries that I wrote as part of TCPlp to handle data buffering. These libraries are intended to replace the mbuf-based data buffering in the FreeBSD TCP implementation. They implement the send buffer as a linked buffer chain (consistent with otLinkedBuffer in OpenThread's TCP API) and the receive buffer as a circular buffer and bitmap (used to keep track of which bytes were received out-of-order). --- third_party/tcplp/CMakeLists.txt | 4 +- third_party/tcplp/Makefile.am | 7 +- third_party/tcplp/lib/bitmap.c | 150 ++++++++++++++++++ third_party/tcplp/lib/bitmap.h | 66 ++++++++ third_party/tcplp/lib/cbuf.c | 264 +++++++++++++++++++++++++++++++ third_party/tcplp/lib/cbuf.h | 115 ++++++++++++++ third_party/tcplp/lib/lbuf.c | 114 +++++++++++++ third_party/tcplp/lib/lbuf.h | 88 +++++++++++ 8 files changed, 806 insertions(+), 2 deletions(-) create mode 100644 third_party/tcplp/lib/bitmap.c create mode 100644 third_party/tcplp/lib/bitmap.h create mode 100644 third_party/tcplp/lib/cbuf.c create mode 100644 third_party/tcplp/lib/cbuf.h create mode 100644 third_party/tcplp/lib/lbuf.c create mode 100644 third_party/tcplp/lib/lbuf.h diff --git a/third_party/tcplp/CMakeLists.txt b/third_party/tcplp/CMakeLists.txt index eafe93aa1..5032a3a52 100644 --- a/third_party/tcplp/CMakeLists.txt +++ b/third_party/tcplp/CMakeLists.txt @@ -29,7 +29,9 @@ project("TCPlp" C) set(src_tcplp - tcplp.c + lib/bitmap.c + lib/cbuf.c + lib/lbuf.c ) set(tcplp_static_target "tcplp") diff --git a/third_party/tcplp/Makefile.am b/third_party/tcplp/Makefile.am index c5beca454..282e8e030 100644 --- a/third_party/tcplp/Makefile.am +++ b/third_party/tcplp/Makefile.am @@ -29,6 +29,9 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am EXTRA_DIST = \ + lib/bitmap.h \ + lib/cbuf.h \ + lib/lbuf.h \ tcplp.h \ $(NULL) @@ -62,7 +65,9 @@ libtcplp_a_CPPFLAGS = \ $(NULL) libtcplp_a_SOURCES = \ - tcplp.c + lib/bitmap.c \ + lib/cbuf.c \ + lib/lbuf.c \ $(NULL) include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/third_party/tcplp/lib/bitmap.c b/third_party/tcplp/lib/bitmap.c new file mode 100644 index 000000000..722859be9 --- /dev/null +++ b/third_party/tcplp/lib/bitmap.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* BITMAP */ + +#include +#include +#include +#include + +#include "bitmap.h" + +void bmp_init(uint8_t* buf, size_t numbytes) { + memset(buf, 0x00, numbytes); +} + +#define _bmp_getrangeinfo(buf, start, len, first_bit_id, first_byte_ptr, last_bit_id, last_byte_ptr) \ + do { \ + first_bit_id = (start & 0x7); \ + first_byte_ptr = buf + (start >> 3); \ + last_bit_id = (len & 0x7) + first_bit_id; \ + last_byte_ptr = first_byte_ptr + (len >> 3) + (last_bit_id >> 3); \ + last_bit_id &= 0x7; \ + } while (0) + +void bmp_setrange(uint8_t* buf, size_t start, size_t len) { + uint8_t first_bit_id; + uint8_t* first_byte_set; + uint8_t last_bit_id; + uint8_t* last_byte_set; + uint8_t first_byte_mask, last_byte_mask; + _bmp_getrangeinfo(buf, start, len, first_bit_id, first_byte_set, + last_bit_id, last_byte_set); + + first_byte_mask = (uint8_t) (0xFF >> first_bit_id); + last_byte_mask = (uint8_t) (0xFF << (8 - last_bit_id)); + + /* Set the bits. */ + if (first_byte_set == last_byte_set) { + *first_byte_set |= (first_byte_mask & last_byte_mask); + } else { + *first_byte_set |= first_byte_mask; + memset(first_byte_set + 1, 0xFF, (size_t) (last_byte_set - first_byte_set - 1)); + *last_byte_set |= last_byte_mask; + } +} + +void bmp_clrrange(uint8_t* buf, size_t start, size_t len) { + uint8_t first_bit_id; + uint8_t* first_byte_clear; + uint8_t last_bit_id; + uint8_t* last_byte_clear; + uint8_t first_byte_mask, last_byte_mask; + _bmp_getrangeinfo(buf, start, len, first_bit_id, first_byte_clear, + last_bit_id, last_byte_clear); + + first_byte_mask = (uint8_t) (0xFF << (8 - first_bit_id)); + last_byte_mask = (uint8_t) (0xFF >> last_bit_id); + + /* Clear the bits. */ + if (first_byte_clear == last_byte_clear) { + *first_byte_clear &= (first_byte_mask | last_byte_mask); + } else { + *first_byte_clear &= first_byte_mask; + memset(first_byte_clear + 1, 0x00, (size_t) (last_byte_clear - first_byte_clear - 1)); + *last_byte_clear &= last_byte_mask; + } +} + +size_t bmp_countset(uint8_t* buf, size_t buflen, size_t start, size_t limit) { + uint8_t first_bit_id; + uint8_t first_byte; + uint8_t ideal_first_byte; + size_t numset; + uint8_t curr_byte; + size_t curr_index = start >> 3; + first_bit_id = start & 0x7; + first_byte = *(buf + curr_index); + + numset = 8 - first_bit_id; // initialize optimistically, assuming that the first byte will have all 1's in the part we care about + ideal_first_byte = (uint8_t) (0xFF >> first_bit_id); + first_byte &= ideal_first_byte; + if (first_byte == ideal_first_byte) { + // All bits in the first byte starting at first_bit_id are set + for (curr_index = curr_index + 1; curr_index < buflen && numset < limit; curr_index++) { + curr_byte = buf[curr_index]; + if (curr_byte == (uint8_t) 0xFF) { + numset += 8; + } else { + while (curr_byte & (uint8_t) 0x80) { // we could add a numset < limit check here, but it probably isn't worth it + curr_byte <<= 1; + numset++; + } + break; + } + } + } else { + // The streak ends within the first byte + do { + first_byte >>= 1; + ideal_first_byte >>= 1; + numset--; + } while (first_byte != ideal_first_byte); + } + return numset; +} + +int bmp_isempty(uint8_t* buf, size_t buflen) { + uint8_t* bufend = buf + buflen; + while (buf < bufend) { + if (*(buf++)) { + return 0; + } + } + return 1; +} + +void bmp_print(uint8_t* buf, size_t buflen) { + size_t i; + for (i = 0; i < buflen; i++) { + printf("%02X", buf[i]); + } + printf("\n"); +} diff --git a/third_party/tcplp/lib/bitmap.h b/third_party/tcplp/lib/bitmap.h new file mode 100644 index 000000000..a4ef41a99 --- /dev/null +++ b/third_party/tcplp/lib/bitmap.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BITMAP_H_ +#define BITMAP_H_ + +#include +#include + +/* Computes the number of bytes required to store the specified number of bits. */ +#define BITS_TO_BYTES(bits) (((bits) >> 3) + (((bits) & 0x7) ? 1 : 0)) + +/* Initializes a bitmap to all zeros. */ +void bmp_init(uint8_t* buf, size_t numbytes); + +/* Sets the specified range of bits. START is the index + of the first bit to be set. LEN is the number of bits + to be set. */ +void bmp_setrange(uint8_t* buf, size_t start, size_t len); + +/* Clears the specified range of bits. START is the index + of the first bit to be cleared. LEN is the number of bits + to be cleared. */ +void bmp_clrrange(uint8_t* buf, size_t start, size_t len); + +/* Counts the number of set bits in BUF starting at START. BUF has length + BUFLEN, in bytes. Counts the number of set bits until it either (1) finds + a bit that isn't set, in which case it returns the number of set bits, + (2) it has counted at least LIMIT bits, in which case it returns a number + greater than or equal to LIMIT, or (3) reaches the end of the buffer, in + which case it returns exactly the number of set bits it found. */ +size_t bmp_countset(uint8_t* buf, size_t buflen, size_t start, size_t limit); + +/* Returns 1 if the bitmap is all zeros, and 0 otherwise. */ +int bmp_isempty(uint8_t* buf, size_t buflen); + +/* Prints out the bitmap in hexadecimal (used for debugging). */ +void bmp_print(uint8_t* buf, size_t buflen); + +#endif diff --git a/third_party/tcplp/lib/cbuf.c b/third_party/tcplp/lib/cbuf.c new file mode 100644 index 000000000..b7793ac65 --- /dev/null +++ b/third_party/tcplp/lib/cbuf.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* CIRCULAR BUFFER */ +#include "cbuf.h" +#include "bitmap.h" + +#include +#include +#include +#include + +#include +#include + +/* + * Copiers for copying from/into cbufs into/from arrays or OpenThraed messages + */ + +void cbuf_copy_into_buffer(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes) { + uint8_t* bufptr = (uint8_t*) buffer; + const uint8_t* arrptr = (const uint8_t*) arr; + memcpy(bufptr + buffer_offset, arrptr + arr_offset, num_bytes); +} + +void cbuf_copy_from_buffer(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes) { + uint8_t* arrptr = (uint8_t*) arr; + const uint8_t* bufptr = (const uint8_t*) buffer; + memcpy(arrptr + arr_offset, bufptr + buffer_offset, num_bytes); +} + +void cbuf_copy_into_message(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes) { + otMessage* message = (otMessage*) buffer; + uint8_t* arrptr = (uint8_t*) arr; + otMessageWrite(message, (uint16_t) buffer_offset, arrptr + arr_offset, (uint16_t) num_bytes); +} + +void cbuf_copy_from_message(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes) { + uint8_t* arrptr = (uint8_t*) arr; + const otMessage* message = (const otMessage*) buffer; + otMessageRead(message, (uint16_t) buffer_offset, arrptr + arr_offset, (uint16_t) num_bytes); +} + +/* + * Cbuf implementation. + */ + +void cbuf_init(struct cbufhead* chdr, uint8_t* buf, size_t len) { + chdr->r_index = 0; + chdr->w_index = 0; + chdr->size = len; + chdr->buf = buf; +} + +size_t cbuf_used_space(struct cbufhead* chdr) { + if (chdr->w_index >= chdr->r_index) { + return chdr->w_index - chdr->r_index; + } else { + return chdr->size + chdr->w_index - chdr->r_index; + } +} + +/* There's always one byte of lost space so I can distinguish between a full + buffer and an empty buffer. */ +size_t cbuf_free_space(struct cbufhead* chdr) { + return chdr->size - 1 - cbuf_used_space(chdr); +} + +size_t cbuf_size(struct cbufhead* chdr) { + return chdr->size - 1; +} + +bool cbuf_empty(struct cbufhead* chdr) { + return (chdr->w_index == chdr->r_index); +} + +size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from) { + size_t free_space = cbuf_free_space(chdr); + uint8_t* buf_data; + size_t fw_index; + size_t bytes_to_end; + if (free_space < data_len) { + data_len = free_space; + } + buf_data = chdr->buf; + fw_index = (chdr->w_index + data_len) % chdr->size; + if (fw_index >= chdr->w_index) { + copy_from(buf_data, chdr->w_index, data, data_offset, data_len); + } else { + bytes_to_end = chdr->size - chdr->w_index; + copy_from(buf_data, chdr->w_index, data, data_offset, bytes_to_end); + copy_from(buf_data, 0, data, data_offset + bytes_to_end, data_len - bytes_to_end); + } + chdr->w_index = fw_index; + return data_len; +} + +void cbuf_read_unsafe(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) { + uint8_t* buf_data = chdr->buf; + size_t fr_index = (chdr->r_index + numbytes) % chdr->size; + size_t bytes_to_end; + if (fr_index >= chdr->r_index) { + copy_into(data, data_offset, buf_data, chdr->r_index, numbytes); + } else { + bytes_to_end = chdr->size - chdr->r_index; + copy_into(data, data_offset, buf_data, chdr->r_index, bytes_to_end); + copy_into(data, data_offset + bytes_to_end, buf_data, 0, numbytes - bytes_to_end); + } + if (pop) { + chdr->r_index = fr_index; + } +} + +size_t cbuf_read(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) { + size_t used_space = cbuf_used_space(chdr); + if (used_space < numbytes) { + numbytes = used_space; + } + cbuf_read_unsafe(chdr, data, data_offset, numbytes, pop, copy_into); + return numbytes; +} + +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) { + size_t used_space = cbuf_used_space(chdr); + size_t oldpos; + if (used_space <= offset) { + return 0; + } else if (used_space < offset + numbytes) { + numbytes = used_space - offset; + } + oldpos = chdr->r_index; + chdr->r_index = (chdr->r_index + offset) % chdr->size; + cbuf_read_unsafe(chdr, data, data_offset, numbytes, 0, copy_into); + chdr->r_index = oldpos; + return numbytes; +} + +size_t cbuf_pop(struct cbufhead* chdr, size_t numbytes) { + size_t used_space = cbuf_used_space(chdr); + if (used_space < numbytes) { + numbytes = used_space; + } + chdr->r_index = (chdr->r_index + numbytes) % chdr->size; + return numbytes; +} + +void cbuf_reference(const struct cbufhead* chdr, otLinkedBuffer* first, otLinkedBuffer* second) { + if (chdr->w_index >= chdr->r_index) { + first->mNext = NULL; + first->mData = &chdr->buf[chdr->r_index]; + first->mLength = (uint16_t) (chdr->w_index - chdr->r_index); + } else { + first->mNext = second; + first->mData = &chdr->buf[chdr->r_index]; + first->mLength = (uint16_t) (chdr->size - chdr->r_index); + + second->mNext = NULL; + second->mData = &chdr->buf[0]; + second->mLength = (uint16_t) chdr->w_index; + } +} + +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) { + uint8_t* buf_data = chdr->buf; + size_t free_space = cbuf_free_space(chdr); + size_t start_index; + size_t end_index; + size_t bytes_to_end; + if (offset > free_space) { + return 0; + } else if (offset + numbytes > free_space) { + numbytes = free_space - offset; + } + start_index = (chdr->w_index + offset) % chdr->size; + end_index = (start_index + numbytes) % chdr->size; + if (end_index >= start_index) { + copy_from(buf_data, start_index, data, data_offset, numbytes); + if (bitmap) { + bmp_setrange(bitmap, start_index, numbytes); + } + } else { + bytes_to_end = chdr->size - start_index; + copy_from(buf_data, start_index, data, data_offset, bytes_to_end); + copy_from(buf_data, 0, data, data_offset + bytes_to_end, numbytes - bytes_to_end); + if (bitmap) { + bmp_setrange(bitmap, start_index, bytes_to_end); + bmp_setrange(bitmap, 0, numbytes - bytes_to_end); + } + } + if (firstindex) { + *firstindex = start_index; + } + return numbytes; +} + +size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap) { + size_t old_w = chdr->w_index; + size_t free_space = cbuf_free_space(chdr); + size_t bytes_to_end; + if (numbytes > free_space) { + numbytes = free_space; + } + chdr->w_index = (chdr->w_index + numbytes) % chdr->size; + if (bitmap) { + if (chdr->w_index >= old_w) { + bmp_clrrange(bitmap, old_w, numbytes); + } else { + bytes_to_end = chdr->size - old_w; + bmp_clrrange(bitmap, old_w, bytes_to_end); + bmp_clrrange(bitmap, 0, numbytes - bytes_to_end); + } + } + return numbytes; +} + +size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit) { + size_t bitmap_size = BITS_TO_BYTES(chdr->size); + size_t until_end; + offset = (chdr->w_index + offset) % chdr->size; + until_end = bmp_countset(bitmap, bitmap_size, offset, limit); + if (until_end >= limit || until_end < (chdr->size - offset)) { + // If we already hit the limit, or if the streak ended before wrapping, then stop here + return until_end; + } + limit -= until_end; // effectively, this is our limit when continuing + // 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) + return until_end + bmp_countset(bitmap, bitmap_size, 0, limit < offset ? limit : offset); +} + +int cbuf_reass_within_offset(struct cbufhead* chdr, size_t offset, size_t index) { + size_t range_start = chdr->w_index; + size_t range_end = (range_start + offset) % chdr->size; + if (range_end >= range_start) { + return index >= range_start && index < range_end; + } else { + return index < range_end || (index >= range_start && index < chdr->size); + } +} diff --git a/third_party/tcplp/lib/cbuf.h b/third_party/tcplp/lib/cbuf.h new file mode 100644 index 000000000..7ba6b6d0f --- /dev/null +++ b/third_party/tcplp/lib/cbuf.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CBUF_H_ +#define CBUF_H_ + +/* CIRCULAR BUFFER */ + +#include +#include +#include +#include + +struct otLinkedBuffer; + +/* Represents a circular buffer. */ +struct cbufhead { + size_t r_index; + size_t w_index; + size_t size; + uint8_t* buf; +}; + +/* Initializes a circular buffer (chdr is the header storing metadata). */ +void cbuf_init(struct cbufhead* chdr, uint8_t* buf, size_t len); + +/* Function type for copying data into or out of a circular buffer. */ +typedef void(*cbuf_copier_t)(void*, size_t, const void*, size_t, size_t); + +/* Instantiations of cbuf_copier_t, for copying data between circular buffers and regular buffers. */ +void cbuf_copy_into_buffer(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes); +void cbuf_copy_from_buffer(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes); + +/* Instantiations of cbuf_copier_t, for copying data between circular buffers and otMessages. */ +void cbuf_copy_into_message(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes); +void cbuf_copy_from_message(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes); + +/* Writes data to the back of the circular buffer using the specified copier. */ +size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from); + +/* Reads data from the front ofthe circular buffer using the specified copier. */ +size_t cbuf_read(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into); + +/* Reads data at the specified offset, in bytes, from the front of the circular buffer using the specified copier. */ +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); + +/* Drops bytes from the front of the circular buffer. */ +size_t cbuf_pop(struct cbufhead* chdr, size_t numbytes); + +/* Returns the number of bytes available for reading from the circular buffer. */ +size_t cbuf_used_space(struct cbufhead* chdr); + +/* Returns the number of bytes that can be written to the circular buffer before it is full. */ +size_t cbuf_free_space(struct cbufhead* chdr); + +/* Returns the total capacity of the circular buffer, in bytes. */ +size_t cbuf_size(struct cbufhead* chdr); + +/* Returns true if the circular buffer is empty, and false if it is not empty. */ +bool cbuf_empty(struct cbufhead* chdr); + +/* Populates the provided otLinkedBuffers to reference the data currently in the circular buffer. */ +void cbuf_reference(const struct cbufhead* chdr, struct otLinkedBuffer* first, struct otLinkedBuffer* second); + +/* Writes DATA at the end of the circular buffer without making it available for + reading. This data is said to be "out-of-sequence". OFFSET is position at + which to write these bytes, relative to the positoin where cbuf_write would + write them. Each bit in the BITMAP corresponds to a byte in the circular + buffer; the bits corresponding to the bytes containing the newly written + data are set. The index of the first byte written is stored into FIRSTINDEX, + if it is not NULL. */ +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); + +/* Makes NUMBYTES out-of-seqence bytes available for reading in the circular + buffer. No data is copied; the out-of-sequence bytes made available are the + bytes currently at the position where cbuf_write would write them. The bytes + are taken from the unused space of the buffer, and can be set using + cbuf_reass_write. */ +size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap); + +/* Counts the number of contiguous out-of-sequence bytes at the specified + OFFSET, until the count reaches the specified LIMIT. */ +size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit); + +/* Returns a true value iff INDEX is the index of a byte within OFFSET bytes + past the end of the buffer. */ +int cbuf_reass_within_offset(struct cbufhead* chdr, size_t offset, size_t index); + +#endif diff --git a/third_party/tcplp/lib/lbuf.c b/third_party/tcplp/lib/lbuf.c new file mode 100644 index 000000000..f4ee16fdc --- /dev/null +++ b/third_party/tcplp/lib/lbuf.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* LINKED BUFFER */ + +#include "lbuf.h" +#include +#include + +void lbuf_init(struct lbufhead* buffer) { + memset(buffer, 0x00, sizeof(struct lbufhead)); +} + +void lbuf_append(struct lbufhead* buffer, otLinkedBuffer* newentry) { + otLinkedBuffer* tail = buffer->tail; + if (tail == NULL) { + buffer->head = newentry; + buffer->tail = newentry; + buffer->offset = 0; + buffer->length = newentry->mLength; + newentry->mNext = NULL; + } else { + tail->mNext = newentry; + buffer->tail = newentry; + buffer->length += newentry->mLength; + newentry->mNext = NULL; + } +} + +void lbuf_extend(struct lbufhead* buffer, size_t numbytes) { + buffer->tail->mLength += numbytes; +} + +size_t lbuf_pop(struct lbufhead* buffer, size_t numbytes, int* ntraversed) { + otLinkedBuffer* curr = buffer->head; + size_t bytesleft = numbytes; + size_t curroffset = buffer->offset; + if (curr == NULL) { + return 0; + } + while (bytesleft >= curr->mLength - curroffset) { + ++*ntraversed; + bytesleft -= (curr->mLength - curroffset); + buffer->length -= (curr->mLength - curroffset); + if (buffer->tail == curr) { + buffer->head = NULL; + buffer->tail = NULL; + buffer->offset = 0; + return numbytes - bytesleft; + } + curr = curr->mNext; + curroffset = 0; + } + /* Handle the last entry. */ + buffer->head = curr; + buffer->offset = curroffset + bytesleft; + buffer->length -= bytesleft; + return numbytes; +} + +int lbuf_getrange(struct lbufhead* buffer, size_t offset, size_t numbytes, + otLinkedBuffer** first, size_t* firstoffset, + otLinkedBuffer** last, size_t* lastextra) { + otLinkedBuffer* curr = buffer->head; + size_t offsetleft = offset + buffer->offset; + size_t bytesleft = numbytes; + if (buffer->length < offset + numbytes) { + return 1; // out of range + } + while (offsetleft > 0 && offsetleft >= curr->mLength) { + offsetleft -= curr->mLength; + curr = curr->mNext; + } + *first = curr; + *firstoffset = offsetleft; + bytesleft += offsetleft; + while (bytesleft > 0 && bytesleft > curr->mLength) { + bytesleft -= curr->mLength; + curr = curr->mNext; + } + *last = curr; + *lastextra = curr->mLength - bytesleft; + return 0; +} + +size_t lbuf_used_space(struct lbufhead* buffer) { + return buffer->length; +} diff --git a/third_party/tcplp/lib/lbuf.h b/third_party/tcplp/lib/lbuf.h new file mode 100644 index 000000000..38b117733 --- /dev/null +++ b/third_party/tcplp/lib/lbuf.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LBUF_H_ +#define LBUF_H_ + +#include +#include + +struct otLinkedBuffer; + +/* LINKED BUFFER */ + +struct lbufhead { + struct otLinkedBuffer* head; + struct otLinkedBuffer* tail; + size_t offset; + size_t length; +}; + +/* Initializes a linked buffer. */ +void lbuf_init(struct lbufhead* buffer); + +/* Returns the contents of the buffer as a linked buffer chain, or NULL if the buffer has + no head. */ +static inline struct otLinkedBuffer* lbuf_head(struct lbufhead* buffer) { + return buffer->head; +} + +/* Adds the contents of NEWENTRY to the buffer by appending it to the end of + the current chain. */ +void lbuf_append(struct lbufhead* buffer, struct otLinkedBuffer* newentry); + +/* Extends the last entry of the buffer by the specified number of bytes. */ +void lbuf_extend(struct lbufhead* buffer, size_t numbytes); + +/* Removes the first NUMBYTES bytes from the buffer, and returns the number of + bytes removed (which is fewer than NUMBYTES if there were fewer than + NUMBYTES bytes in the buffer to begin with). *NTRAVERSED is incremented once + for each entry in the buffer that is no longer referenced and can be + reclaimed. */ +size_t lbuf_pop(struct lbufhead* buffer, size_t numbytes, int* ntraversed); + +/* Given a range of indices, specified by an OFFSET from the start and a + length NUMBYTES, this function locates the chain of linked buffer entries + that reference the corresponding bytes. + A pointer to the first entry in the range is stored into FIRST, and the + number of bytes in the entry before the start of the range is stored into + FIRSTOFFSET. A pointer to the last entry in the range is stored into LAST, + and the number of bytes in that entry after the end of the range is stored + into LASTEXTRA. + Returns 0 on success and 1 on failure. On failure, FIRST, LAST, FIRSTOFFSET, + and LASTEXTRA are not set. The only failure condition is when there are not + enough bytes in the buffer to do the full traversal. */ +int lbuf_getrange(struct lbufhead* buffer, size_t offset, size_t numbytes, + struct otLinkedBuffer** first, size_t* firstoffset, + struct otLinkedBuffer** last, size_t* lastextra); + +/* Returns the total number of bytes stored in the buffer. */ +size_t lbuf_used_space(struct lbufhead* buffer); + +#endif