Added a simple net_buffer implementation (using a single malloc()ed

buffer) for debugging/testing purposes.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25254 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-04-29 19:05:50 +00:00
parent 0e8836d284
commit d96c90096f
3 changed files with 754 additions and 0 deletions

View File

@ -26,6 +26,9 @@ KernelAddon stack :
stack.cpp
stack_interface.cpp
utility.cpp
# for test purposes
#simple_net_buffer.cpp
;
# Installation

View File

@ -0,0 +1,727 @@
/*
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#include "simple_net_buffer.h"
#include "utility.h"
#include <net_buffer.h>
#include <slab/Slab.h>
#include <tracing.h>
#include <util/list.h>
#include <ByteOrder.h>
#include <debug.h>
#include <KernelExport.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include "paranoia_config.h"
//#define TRACE_BUFFER
#ifdef TRACE_BUFFER
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define MAX_ANCILLARY_DATA_SIZE 128
struct ancillary_data : DoublyLinkedListLinkImpl<ancillary_data> {
void* Data()
{
return (char*)this + _ALIGN(sizeof(ancillary_data));
}
static ancillary_data* FromData(void* data)
{
return (ancillary_data*)((char*)data - _ALIGN(sizeof(ancillary_data)));
}
ancillary_data_header header;
void (*destructor)(const ancillary_data_header*, void*);
};
typedef DoublyLinkedList<ancillary_data> ancillary_data_list;
struct net_buffer_private : simple_net_buffer {
ancillary_data_list ancillary_data;
};
static status_t append_data(net_buffer *buffer, const void *data, size_t size);
static status_t trim_data(net_buffer *_buffer, size_t newSize);
static status_t remove_header(net_buffer *_buffer, size_t bytes);
static status_t remove_trailer(net_buffer *_buffer, size_t bytes);
static void
copy_metadata(net_buffer *destination, const net_buffer *source)
{
memcpy(destination->source, source->source,
min_c(source->source->sa_len, sizeof(sockaddr_storage)));
memcpy(destination->destination, source->destination,
min_c(source->destination->sa_len, sizeof(sockaddr_storage)));
destination->flags = source->flags;
destination->interface = source->interface;
destination->offset = source->offset;
destination->size = source->size;
destination->protocol = source->protocol;
destination->type = source->type;
}
// #pragma mark - module API
static net_buffer *
create_buffer(size_t headerSpace)
{
net_buffer_private *buffer = new(nothrow) net_buffer_private;
if (buffer == NULL)
return NULL;
TRACE(("%ld: create buffer %p\n", find_thread(NULL), buffer));
buffer->data = NULL;
new(&buffer->ancillary_data) ancillary_data_list;
buffer->source = (sockaddr *)&buffer->storage.source;
buffer->destination = (sockaddr *)&buffer->storage.destination;
buffer->storage.source.ss_len = 0;
buffer->storage.destination.ss_len = 0;
buffer->interface = NULL;
buffer->offset = 0;
buffer->flags = 0;
buffer->size = 0;
buffer->type = -1;
return buffer;
}
static void
free_buffer(net_buffer *_buffer)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
free(buffer->data);
delete buffer;
}
/*! Creates a duplicate of the \a buffer. The new buffer does not share internal
storage; they are completely independent from each other.
*/
static net_buffer *
duplicate_buffer(net_buffer *_buffer)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
net_buffer* duplicate = create_buffer(0);
if (duplicate == NULL)
return NULL;
if (append_data(duplicate, buffer->data, buffer->size) != B_OK) {
free_buffer(duplicate);
return NULL;
}
copy_metadata(duplicate, buffer);
return duplicate;
}
/*! Clones the buffer by grabbing another reference to the underlying data.
If that data changes, it will be changed in the clone as well.
If \a shareFreeSpace is \c true, the cloned buffer may claim the free
space in the original buffer as the original buffer can still do. If you
are using this, it's your responsibility that only one of the buffers
will do this.
*/
static net_buffer *
clone_buffer(net_buffer *_buffer, bool shareFreeSpace)
{
return duplicate_buffer(_buffer);
}
/*!
Split the buffer at offset, the header data
is returned as new buffer.
TODO: optimize and avoid making a copy.
*/
static net_buffer *
split_buffer(net_buffer *_from, uint32 offset)
{
net_buffer_private *from = (net_buffer_private *)_from;
if (offset > from->size)
return NULL;
net_buffer_private* buffer = (net_buffer_private*)create_buffer(0);
if (buffer == NULL)
return NULL;
// allocate space for the tail data
size_t remaining = from->size - offset;
uint8* tailData = (uint8*)malloc(remaining);
if (tailData == NULL) {
free_buffer(buffer);
return NULL;
}
memcpy(tailData, from->data + offset, remaining);
// truncate original data and move it to the new buffer
buffer->data = (uint8*)realloc(from->data, offset);
buffer->size = offset;
// the old buffer gets the newly allocated tail data
from->data = tailData;
from->size = remaining;
return buffer;
}
/*!
Merges the second buffer with the first. If \a after is \c true, the
second buffer's contents will be appended to the first ones, else they
will be prepended.
The second buffer will be freed if this function succeeds.
*/
static status_t
merge_buffer(net_buffer *_buffer, net_buffer *_with, bool after)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
net_buffer_private *with = (net_buffer_private *)_with;
if (with == NULL)
return B_BAD_VALUE;
if (after) {
// the simple case: just append the second buffer
status_t error = append_data(buffer, with->data, with->size);
if (error != B_OK)
return error;
} else {
// append buffer to the second buffer, then switch the data
status_t error = append_data(with, buffer->data, buffer->size);
if (error != B_OK)
return error;
free(buffer->data);
buffer->data = with->data;
buffer->size = with->size;
with->data = NULL;
}
free_buffer(with);
return B_OK;
}
/*! Writes into existing allocated memory.
\return B_BAD_VALUE if you write outside of the buffers current
bounds.
*/
static status_t
write_data(net_buffer *_buffer, size_t offset, const void *data, size_t size)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (offset + size > buffer->size)
return B_BAD_VALUE;
if (size == 0)
return B_OK;
memcpy(buffer->data + offset, data, size);
return B_OK;
}
static status_t
read_data(net_buffer *_buffer, size_t offset, void *data, size_t size)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (offset + size > buffer->size)
return B_BAD_VALUE;
if (size == 0)
return B_OK;
memcpy(data, buffer->data + offset, size);
return B_OK;
}
static status_t
prepend_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
{
if (size == 0)
return B_OK;
net_buffer_private *buffer = (net_buffer_private *)_buffer;
uint8* newData = (uint8*)malloc(buffer->size + size);
if (newData == NULL)
return B_NO_MEMORY;
memcpy(newData + size, buffer->data, buffer->size);
free(buffer->data);
buffer->data = newData;
buffer->size += size;
if (_contiguousBuffer != NULL)
*_contiguousBuffer = buffer->data;
return B_OK;
}
static status_t
prepend_data(net_buffer *buffer, const void *data, size_t size)
{
status_t status = prepend_size(buffer, size, NULL);
if (status < B_OK)
return status;
write_data(buffer, 0, data, size);
return B_OK;
}
static status_t
append_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
{
if (size == 0)
return B_OK;
net_buffer_private *buffer = (net_buffer_private *)_buffer;
uint8* newData = (uint8*)realloc(buffer->data, buffer->size + size);
if (newData == NULL)
return B_NO_MEMORY;
if (_contiguousBuffer != NULL)
*_contiguousBuffer = newData + buffer->size;
buffer->data = newData;
buffer->size += size;
return B_OK;
}
static status_t
append_data(net_buffer *buffer, const void *data, size_t size)
{
size_t used = buffer->size;
status_t status = append_size(buffer, size, NULL);
if (status < B_OK)
return status;
write_data(buffer, used, data, size);
return B_OK;
}
/*!
Removes bytes from the beginning of the buffer.
*/
static status_t
remove_header(net_buffer *_buffer, size_t bytes)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (bytes > buffer->size)
return B_BAD_VALUE;
if (bytes == 0)
return B_OK;
buffer->size -= bytes;
memmove(buffer->data, buffer->data + bytes, buffer->size);
buffer->data = (uint8*)realloc(buffer->data, buffer->size);
return B_OK;
}
/*!
Removes bytes from the end of the buffer.
*/
static status_t
remove_trailer(net_buffer *buffer, size_t bytes)
{
return trim_data(buffer, buffer->size - bytes);
}
/*!
Trims the buffer to the specified \a newSize by removing space from
the end of the buffer.
*/
static status_t
trim_data(net_buffer *_buffer, size_t newSize)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (newSize > buffer->size)
return B_BAD_VALUE;
if (newSize == buffer->size)
return B_OK;
buffer->data = (uint8*)realloc(buffer->data, newSize);
buffer->size = newSize;
return B_OK;
}
/*!
Appends data coming from buffer \a source to the buffer \a buffer. It only
clones the data, though, that is the data is not copied, just referenced.
*/
static status_t
append_cloned_data(net_buffer *_buffer, net_buffer *_source, uint32 offset,
size_t bytes)
{
if (bytes == 0)
return B_OK;
net_buffer_private *buffer = (net_buffer_private *)_buffer;
net_buffer_private *source = (net_buffer_private *)_source;
if (offset + bytes > source->size)
return B_BAD_VALUE;
return append_data(buffer, source->data + offset, bytes);
}
/*!
Attaches ancillary data to the given buffer. The data are completely
orthogonal to the data the buffer stores.
\param buffer The buffer.
\param header Description of the data.
\param data If not \c NULL, the data are copied into the allocated storage.
\param destructor If not \c NULL, this function will be invoked with the
data as parameter when the buffer is destroyed.
\param _allocatedData Will be set to the storage allocated for the data.
\return \c B_OK when everything goes well, another error code otherwise.
*/
static status_t
attach_ancillary_data(net_buffer *_buffer, const ancillary_data_header *header,
const void *data, void (*destructor)(const ancillary_data_header*, void*),
void **_allocatedData)
{
// TODO: Obviously it would be nice to allocate the memory for the
// ancillary data in the buffer.
net_buffer_private *buffer = (net_buffer_private *)_buffer;
// check parameters
if (header == NULL)
return B_BAD_VALUE;
if (header->len > MAX_ANCILLARY_DATA_SIZE)
return ENOBUFS;
// allocate buffer
void *dataBuffer = malloc(_ALIGN(sizeof(ancillary_data)) + header->len);
if (dataBuffer == NULL)
return B_NO_MEMORY;
// init and attach the structure
ancillary_data *ancillaryData = new(dataBuffer) ancillary_data;
ancillaryData->header = *header;
ancillaryData->destructor = destructor;
buffer->ancillary_data.Add(ancillaryData);
if (data != NULL)
memcpy(ancillaryData->Data(), data, header->len);
if (_allocatedData != NULL)
*_allocatedData = ancillaryData->Data();
return B_OK;
}
/*!
Detaches ancillary data from the given buffer. The associated memory is
free, i.e. the \a data pointer must no longer be used after calling this
function. Depending on \a destroy, the destructor is invoked before freeing
the data.
\param buffer The buffer.
\param data Pointer to the data to be removed (as returned by
attach_ancillary_data() or next_ancillary_data()).
\param destroy If \c true, the destructor, if one was passed to
attach_ancillary_data(), is invoked for the data.
\return \c B_OK when everything goes well, another error code otherwise.
*/
static status_t
detach_ancillary_data(net_buffer *_buffer, void *data, bool destroy)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (data == NULL)
return B_BAD_VALUE;
ancillary_data *ancillaryData = ancillary_data::FromData(data);
buffer->ancillary_data.Remove(ancillaryData);
if (destroy && ancillaryData->destructor != NULL) {
ancillaryData->destructor(&ancillaryData->header,
ancillaryData->Data());
}
free(ancillaryData);
return B_OK;
}
/*!
Moves all ancillary data from buffer \c from to the end of the list of
ancillary data of buffer \c to. Note, that this is the only function that
transfers or copies ancillary data from one buffer to another.
\param from The buffer from which to remove the ancillary data.
\param to The buffer to which to add teh ancillary data.
\return A pointer to the first of the moved ancillary data, if any, \c NULL
otherwise.
*/
static void *
transfer_ancillary_data(net_buffer *_from, net_buffer *_to)
{
net_buffer_private *from = (net_buffer_private *)_from;
net_buffer_private *to = (net_buffer_private *)_to;
if (from == NULL || to == NULL)
return NULL;
ancillary_data *ancillaryData = from->ancillary_data.Head();
to->ancillary_data.MoveFrom(&from->ancillary_data);
return ancillaryData != NULL ? ancillaryData->Data() : NULL;
}
/*!
Returns the next ancillary data. When iterating over the data, initially
a \c NULL pointer shall be passed as \a previousData, subsequently the
previously returned data pointer. After the last item, \c NULL is returned.
Note, that it is not safe to call detach_ancillary_data() for a data item
and then pass that pointer to this function. First get the next item, then
detach the previous one.
\param buffer The buffer.
\param previousData The pointer to the previous data returned by this
function. Initially \c NULL shall be passed.
\param header Pointer to allocated storage into which the data description
is written. May be \c NULL.
\return A pointer to the next ancillary data in the buffer. \c NULL after
the last one.
*/
static void*
next_ancillary_data(net_buffer *_buffer, void *previousData,
ancillary_data_header *_header)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
ancillary_data *ancillaryData;
if (previousData == NULL) {
ancillaryData = buffer->ancillary_data.Head();
} else {
ancillaryData = ancillary_data::FromData(previousData);
ancillaryData = buffer->ancillary_data.GetNext(ancillaryData);
}
if (ancillaryData == NULL)
return NULL;
if (_header != NULL)
*_header = ancillaryData->header;
return ancillaryData->Data();
}
/*!
Tries to directly access the requested space in the buffer.
If the space is contiguous, the function will succeed and place a pointer
to that space into \a _contiguousBuffer.
\return B_BAD_VALUE if the offset is outside of the buffer's bounds.
\return B_ERROR in case the buffer is not contiguous at that location.
*/
static status_t
direct_access(net_buffer *_buffer, uint32 offset, size_t size,
void **_contiguousBuffer)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (offset + size > buffer->size)
return B_BAD_VALUE;
*_contiguousBuffer = buffer->data + offset;
return B_OK;
}
static int32
checksum_data(net_buffer *_buffer, uint32 offset, size_t size, bool finalize)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (offset + size > buffer->size || size == 0)
return B_BAD_VALUE;
uint16 sum = compute_checksum(buffer->data + offset, size);
if ((offset & 1) != 0) {
// if we're at an uneven offset, we have to swap the checksum
sum = __swap_int16(sum);
}
if (!finalize)
return (uint16)sum;
return (uint16)~sum;
}
static uint32
get_iovecs(net_buffer *_buffer, struct iovec *iovecs, uint32 vecCount)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
iovecs[0].iov_base = buffer->data;
iovecs[0].iov_len = buffer->size;
return 1;
}
static uint32
count_iovecs(net_buffer *_buffer)
{
return 1;
}
static void
swap_addresses(net_buffer *buffer)
{
std::swap(buffer->source, buffer->destination);
}
static void
dump_buffer(net_buffer *_buffer)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
dprintf("buffer %p, size %ld, data: %p\n", buffer, buffer->size,
buffer->data);
dump_block((char*)buffer->data, min_c(buffer->size, 32), " ");
}
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
return B_OK;
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
}
net_buffer_module_info gSimpleNetBufferModule = {
//net_buffer_module_info gNetBufferModule = {
{
NET_BUFFER_MODULE_NAME,
0,
std_ops
},
create_buffer,
free_buffer,
duplicate_buffer,
clone_buffer,
split_buffer,
merge_buffer,
prepend_size,
prepend_data,
append_size,
append_data,
NULL, // insert
NULL, // remove
remove_header,
remove_trailer,
trim_data,
append_cloned_data,
NULL, // associate_data
attach_ancillary_data,
detach_ancillary_data,
transfer_ancillary_data,
next_ancillary_data,
direct_access,
read_data,
write_data,
checksum_data,
NULL, // get_memory_map
get_iovecs,
count_iovecs,
swap_addresses,
dump_buffer, // dump
};

View File

@ -0,0 +1,24 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef SIMPLE_NET_BUFFER_H
#define SIMPLE_NET_BUFFER_H
#include <net_buffer.h>
struct simple_net_buffer : net_buffer {
uint8* data;
struct {
struct sockaddr_storage source;
struct sockaddr_storage destination;
} storage;
};
extern net_buffer_module_info gSimpleNetBufferModule;
#endif // SIMPLE_NET_BUFFER_H