[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).
This commit is contained in:
Sam Kumar
2021-09-24 23:15:40 -07:00
committed by GitHub
parent 39fca69650
commit 5564ea0530
8 changed files with 806 additions and 2 deletions
+3 -1
View File
@@ -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")
+6 -1
View File
@@ -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
+150
View File
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2018, Sam Kumar <[email protected]>
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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");
}
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018, Sam Kumar <[email protected]>
* 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 <stdint.h>
#include <stdlib.h>
/* 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
+264
View File
@@ -0,0 +1,264 @@
/*
* Copyright (c) 2018, Sam Kumar <[email protected]>
* 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <openthread/message.h>
#include <openthread/tcp.h>
/*
* 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);
}
}
+115
View File
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2018, Sam Kumar <[email protected]>
* 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 <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
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
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2018, Sam Kumar <[email protected]>
* 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 <string.h>
#include <openthread/tcp.h>
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;
}
+88
View File
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2018, Sam Kumar <[email protected]>
* 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 <stddef.h>
#include <stdint.h>
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