xref: /haiku/src/add-ons/kernel/network/stack/net_buffer.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "utility.h"
11 
12 #include <net_buffer.h>
13 #include <util/list.h>
14 
15 #include <ByteOrder.h>
16 #include <KernelExport.h>
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/uio.h>
21 
22 
23 #define TRACE_BUFFER
24 #ifdef TRACE_BUFFER
25 #	define TRACE(x) dprintf x
26 #else
27 #	define TRACE(x) ;
28 #endif
29 
30 #define BUFFER_SIZE 2048
31 
32 struct data_node {
33 	struct list_link link;
34 	struct data_header *header;
35 	size_t		offset;			// the net_buffer-wide offset of this node
36 	uint8		*start;			// points to the start of the data
37 	size_t		used;			// defines how much memory is used by this node
38 	size_t		header_space;
39 	size_t		tail_space;
40 };
41 
42 struct data_header {
43 	int32		ref_count;
44 	addr_t		physical_address;
45 	size_t		size;
46 	uint8		*data_end;
47 	size_t		data_space;
48 	data_node	*first_node;
49 };
50 
51 struct net_buffer_private : net_buffer {
52 	struct list	buffers;
53 	data_node	first_node;
54 };
55 
56 
57 static status_t append_data(net_buffer *buffer, const void *data, size_t size);
58 static status_t trim_data(net_buffer *_buffer, size_t newSize);
59 static status_t remove_header(net_buffer *_buffer, size_t bytes);
60 static status_t remove_trailer(net_buffer *_buffer, size_t bytes);
61 
62 
63 #if 0
64 static void
65 dump_buffer(net_buffer *_buffer)
66 {
67 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
68 
69 	dprintf("buffer %p, size %ld\n", buffer, buffer->size);
70 	data_node *node = NULL;
71 	while ((node = (data_node *)list_get_next_item(&buffer->buffers, node)) != NULL) {
72 		dprintf("  node %p, offset %ld, used %ld, header %ld, tail %ld, header %p\n",
73 			node, node->offset, node->used, node->header_space, node->tail_space, node->header);
74 		//dump_block((char *)node->start, node->used, "    ");
75 		dump_block((char *)node->start, min_c(node->used, 32), "    ");
76 	}
77 }
78 #endif
79 
80 
81 static data_header *
82 create_data_header(size_t size, size_t headerSpace)
83 {
84 	// TODO: don't use malloc!
85 	data_header *header = (data_header *)malloc(size);
86 	if (header == NULL)
87 		return NULL;
88 
89 	header->ref_count = 1;
90 	header->physical_address = 0;
91 		// TODO: initialize this correctly
92 	header->size = size;
93 	header->data_space = headerSpace;
94 	header->data_end = (uint8 *)header + sizeof(struct data_header);
95 	header->first_node = NULL;
96 
97 	TRACE(("  create new data header %p\n", header));
98 	return header;
99 }
100 
101 
102 static void
103 release_data_header(data_header *header)
104 {
105 	if (atomic_add(&header->ref_count, -1) != 1)
106 		return;
107 
108 	TRACE(("  free header %p\n", header));
109 	free(header);
110 }
111 
112 
113 inline void
114 acquire_data_header(data_header *header)
115 {
116 	atomic_add(&header->ref_count, 1);
117 }
118 
119 
120 static void
121 free_data_header_space(data_header *header, uint8 *data, size_t size)
122 {
123 	if (header->data_end != data + size) {
124 		// this wasn't the last allocation, unfortunately, there is nothing
125 		// to do for us, then
126 		// TODO: if the need arises, a simple free list could do wonder
127 		// TODO: remove_data_node() currently calls this function no matter
128 		//	where the node had been placed - this would need to be changed
129 		//	then, too.
130 		return;
131 	}
132 
133 	header->data_end -= size;
134 	header->data_space += size;
135 }
136 
137 
138 static uint8 *
139 alloc_data_header_space(data_header *header, size_t size)
140 {
141 	if (header->data_space < size)
142 		return NULL;
143 
144 	uint8 *data = header->data_end;
145 	header->data_end += size;
146 	header->data_space -= size;
147 
148 	if (header->first_node != NULL)
149 		header->first_node->header_space -= size;
150 #if 0
151 	else
152 		dprintf("add data to a header without first node - could overwrite something!\n");
153 #endif
154 
155 	return data;
156 }
157 
158 
159 static void
160 init_data_node(data_node *node, data_header *header, size_t headerSpace)
161 {
162 	node->header = header;
163 	node->offset = 0;
164 	node->start = (uint8 *)header + sizeof(data_header) + headerSpace;
165 	node->used = 0;
166 	node->header_space = headerSpace;
167 	node->tail_space = header->size - headerSpace - sizeof(data_header);
168 }
169 
170 
171 static data_node *
172 add_data_node(data_header *header)
173 {
174 	data_node *node = (data_node *)alloc_data_header_space(header, sizeof(data_node));
175 	if (node == NULL)
176 		return NULL;
177 
178 	TRACE(("  add data node %p to header %p\n", node, header));
179 	acquire_data_header(header);
180 	memset(node, 0, sizeof(struct data_node));
181 	return node;
182 }
183 
184 
185 void
186 remove_data_node(data_node *node)
187 {
188 	data_header *header = node->header;
189 
190 	TRACE(("  remove data node %p from header %p\n", node, header));
191 	free_data_header_space(header, (uint8 *)node, sizeof(data_node));
192 	if (header->first_node == node)
193 		header->first_node = NULL;
194 
195 	release_data_header(node->header);
196 }
197 
198 
199 //	#pragma mark -
200 
201 
202 static net_buffer *
203 create_buffer(size_t headerSpace)
204 {
205 	net_buffer_private *buffer = (net_buffer_private *)malloc(sizeof(struct net_buffer_private));
206 	if (buffer == NULL)
207 		return NULL;
208 
209 	TRACE(("create buffer %p\n", buffer));
210 
211 	data_header *header = create_data_header(BUFFER_SIZE, headerSpace);
212 	if (header == NULL) {
213 		free(buffer);
214 		return NULL;
215 	}
216 
217 	init_data_node(&buffer->first_node, header, headerSpace);
218 	header->first_node = &buffer->first_node;
219 
220 	list_init(&buffer->buffers);
221 	list_add_item(&buffer->buffers, &buffer->first_node);
222 
223 	buffer->source.ss_len = 0;
224 	buffer->destination.ss_len = 0;
225 	buffer->interface = NULL;
226 	buffer->flags = 0;
227 	buffer->size = 0;
228 
229 	return buffer;
230 }
231 
232 
233 static void
234 free_buffer(net_buffer *_buffer)
235 {
236 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
237 
238 	TRACE(("free buffer %p\n", buffer));
239 
240 	data_node *node;
241 	while ((node = (data_node *)list_remove_head_item(&buffer->buffers)) != NULL) {
242 		remove_data_node(node);
243 	}
244 
245 	free(buffer);
246 }
247 
248 
249 /*!	Creates a duplicate of the \a buffer. The new buffer does not share internal
250 	storage; they are completely independent from each other.
251 */
252 static net_buffer *
253 duplicate_buffer(net_buffer *_buffer)
254 {
255 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
256 
257 	net_buffer *duplicate = create_buffer(buffer->first_node.header_space);
258 	if (duplicate == NULL)
259 		return NULL;
260 
261 	// copy the data from the source buffer
262 
263 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
264 	while (true) {
265 		if (append_data(duplicate, node->start, node->used) < B_OK) {
266 			free_buffer(duplicate);
267 			return NULL;
268 		}
269 
270 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
271 		if (node == NULL)
272 			break;
273 	}
274 
275 	// copy meta data from source buffer
276 
277 	memcpy(&duplicate->source, &buffer->source, buffer->source.ss_len);
278 	memcpy(&duplicate->destination, &buffer->destination, buffer->destination.ss_len);
279 
280 	duplicate->flags = buffer->flags;
281 	duplicate->interface = buffer->interface;
282 	duplicate->size = buffer->size;
283 	duplicate->protocol = buffer->protocol;
284 
285 	return duplicate;
286 }
287 
288 
289 /*!	Clones the buffer by grabbing another reference to the underlying data.
290 	If that data changes, it will be changed in the clone as well.
291 
292 	If \a shareFreeSpace is \c true, the cloned buffer may claim the free
293 	space in the original buffer as the original buffer can still do. If you
294 	are using this, it's your responsibility that only one of the buffers
295 	will do this.
296 */
297 static net_buffer *
298 clone_buffer(net_buffer *_buffer, bool shareFreeSpace)
299 {
300 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
301 
302 	net_buffer_private *clone = (net_buffer_private *)malloc(sizeof(struct net_buffer_private));
303 	if (clone == NULL)
304 		return NULL;
305 
306 	data_node *node = &clone->first_node;
307 	data_node *sourceNode = (data_node *)list_get_first_item(&buffer->buffers);
308 	if (sourceNode == NULL) {
309 		free(clone);
310 		return NULL;
311 	}
312 
313 	list_init(&clone->buffers);
314 
315 	// grab reference to this buffer - all additional nodes will get
316 	// theirs in add_data_node()
317 	atomic_add(&sourceNode->header->ref_count, 1);
318 
319 	while (sourceNode != NULL) {
320 		node->header = sourceNode->header;
321 		node->start = sourceNode->start;
322 		node->used = sourceNode->used;
323 		node->offset = sourceNode->offset;
324 
325 		if (shareFreeSpace) {
326 			// both buffers could claim the free space - note that this option
327 			// has to be used carefully
328 			node->header_space = sourceNode->header_space;
329 			node->tail_space = sourceNode->tail_space;
330 		} else {
331 			// the free space stays with the original buffer
332 			node->header_space = 0;
333 			node->tail_space = 0;
334 		}
335 
336 		// add node to clone's list of buffers
337 		list_add_item(&clone->buffers, node);
338 
339 		sourceNode = (data_node *)list_get_next_item(&buffer->buffers, sourceNode);
340 		if (sourceNode == NULL)
341 			break;
342 
343 		node = add_data_node(sourceNode->header);
344 		if (node == NULL) {
345 			// There was not enough space left for another node in this buffer
346 			// TODO: handle this case!
347 			panic("clone buffer hits size limit... (fix me)");
348 			free(clone);
349 			return NULL;
350 		}
351 	}
352 
353 	// copy meta data from source buffer
354 
355 	memcpy(&clone->source, &buffer->source, buffer->source.ss_len);
356 	memcpy(&clone->destination, &buffer->destination, buffer->destination.ss_len);
357 
358 	clone->flags = buffer->flags;
359 	clone->interface = buffer->interface;
360 	clone->size = buffer->size;
361 	clone->protocol = buffer->protocol;
362 
363 	return clone;
364 }
365 
366 
367 /*!
368 	Split the buffer at offset, the header data
369 	is returned as new buffer.
370 	TODO: optimize and avoid making a copy.
371 */
372 static net_buffer *
373 split_buffer(net_buffer *from, uint32 offset)
374 {
375 	net_buffer *buffer = duplicate_buffer(from);
376 	if (buffer == NULL)
377 		return NULL;
378 
379 	TRACE(("split_buffer(buffer %p -> %p, offset %ld)\n", from, buffer, offset));
380 
381 	if (remove_header(from, offset) == B_OK
382 		&& trim_data(buffer, offset) == B_OK)
383 		return buffer;
384 
385 	free_buffer(buffer);
386 	return NULL;
387 }
388 
389 
390 /*!
391 	Merges the second buffer with the first. If \a after is \c true, the
392 	second buffer's contents will be appended to the first ones, else they
393 	will be prepended.
394 	The second buffer will be freed if this function succeeds.
395 */
396 static status_t
397 merge_buffer(net_buffer *_buffer, net_buffer *_with, bool after)
398 {
399 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
400 	net_buffer_private *with = (net_buffer_private *)_with;
401 	if (with == NULL)
402 		return B_BAD_VALUE;
403 
404 	TRACE(("merge buffer %p with %p (%s)\n", buffer, with, after ? "after" : "before"));
405 	//dump_buffer(buffer);
406 	//dprintf("with:\n");
407 	//dump_buffer(with);
408 
409 	// TODO: this is currently very simplistic, I really need to finish the
410 	//	harder part of this implementation (data_node management per header)
411 
412 	data_node *before = NULL;
413 
414 	if (!after) {
415 		// change offset of all nodes already in the buffer
416 		data_node *node = NULL;
417 		while (true) {
418 			node = (data_node *)list_get_next_item(&buffer->buffers, node);
419 			if (node == NULL)
420 				break;
421 
422 			node->offset += with->size;
423 			if (before == NULL)
424 				before = node;
425 		}
426 	}
427 
428 	data_node *last = NULL;
429 
430 	while (true) {
431 		data_node *node = (data_node *)list_get_next_item(&with->buffers, last);
432 		if (node == NULL)
433 			break;
434 
435 		if ((uint8 *)node > (uint8 *)node->header
436 			&& (uint8 *)node < (uint8 *)node->header + node->header->size) {
437 			// The node is already in the buffer, we can just move it
438 			// over to the new owner
439 			list_remove_item(&with->buffers, node);
440 		} else {
441 			// we need a new place for this node
442 			data_node *newNode = add_data_node(node->header);
443 			if (newNode == NULL) {
444 // TODO: this can't work right now as add_data_node() also grabs a reference
445 //		to the header - but in this case, we would need two references, one
446 //		for the data, one for the node, and there is no mechanism for this.
447 #if 0
448 				// try again on the buffers own header
449 				newNode = add_data_node(buffer->first_node.header);
450 				if (newNode == NULL)
451 #endif
452 // TODO: try to revert buffers to their initial state!!
453 				return ENOBUFS;
454 			}
455 
456 			last = node;
457 			*newNode = *node;
458 			node = newNode;
459 				// the old node will get freed with its buffer
460 		}
461 
462 		if (after) {
463 			list_add_item(&buffer->buffers, node);
464 			node->offset = buffer->size;
465 		} else
466 			list_insert_item_before(&buffer->buffers, before, node);
467 
468 		buffer->size += node->used;
469 	}
470 
471 	// the data has been merged completely at this point
472 	free_buffer(with);
473 
474 	//dprintf(" merge result:\n");
475 	//dump_buffer(buffer);
476 	return B_OK;
477 }
478 
479 
480 /*!	Writes into existing allocated memory.
481 	\return B_BAD_VALUE if you write outside of the buffers current
482 		bounds.
483 */
484 static status_t
485 write_data(net_buffer *_buffer, size_t offset, const void *data, size_t size)
486 {
487 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
488 
489 	if (offset + size > buffer->size)
490 		return B_BAD_VALUE;
491 	if (size == 0)
492 		return B_OK;
493 
494 	// find first node to write into
495 
496 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
497 	while (node->offset + node->used < offset) {
498 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
499 		if (node == NULL)
500 			return B_BAD_VALUE;
501 	}
502 
503 	offset -= node->offset;
504 
505 	while (true) {
506 		size_t written = min_c(size, node->used - offset);
507 		memcpy(node->start + offset, data, written);
508 
509 		size -= written;
510 		if (size == 0)
511 			break;
512 
513 		offset = 0;
514 		data = (void *)((uint8 *)data + written);
515 
516 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
517 		if (node == NULL)
518 			return B_BAD_VALUE;
519 	}
520 
521 	return B_OK;
522 }
523 
524 
525 static status_t
526 read_data(net_buffer *_buffer, size_t offset, void *data, size_t size)
527 {
528 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
529 
530 	if (offset + size > buffer->size)
531 		return B_BAD_VALUE;
532 	if (size == 0)
533 		return B_OK;
534 
535 	// find first node to read from
536 
537 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
538 	while (node->offset + node->used < offset) {
539 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
540 		if (node == NULL)
541 			return B_BAD_VALUE;
542 	}
543 
544 	offset -= node->offset;
545 
546 	while (true) {
547 		size_t bytesRead = min_c(size, node->used - offset);
548 		memcpy(data, node->start + offset, bytesRead);
549 
550 		size -= bytesRead;
551 		if (size == 0)
552 			break;
553 
554 		offset = 0;
555 		data = (void *)((uint8 *)data + bytesRead);
556 
557 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
558 		if (node == NULL)
559 			return B_BAD_VALUE;
560 	}
561 
562 	return B_OK;
563 }
564 
565 
566 static status_t
567 prepend_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
568 {
569 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
570 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
571 
572 	TRACE(("prepend_size(buffer %p, size %ld)\n", buffer, size));
573 	//dump_buffer(buffer);
574 
575 	if (node->header_space < size) {
576 		// we need to prepend a new buffer
577 
578 		// TODO: implement me!
579 		panic("prepending buffer not implemented\n");
580 
581 		if (_contiguousBuffer)
582 			*_contiguousBuffer = NULL;
583 
584 		return B_ERROR;
585 	}
586 
587 	// the data fits into this buffer
588 	node->header_space -= size;
589 	node->start -= size;
590 	node->used += size;
591 
592 	if (_contiguousBuffer)
593 		*_contiguousBuffer = node->start;
594 
595 	// adjust offset of following nodes
596 	while ((node = (data_node *)list_get_next_item(&buffer->buffers, node)) != NULL) {
597 		node->offset += size;
598 	}
599 
600 	buffer->size += size;
601 
602 	//dprintf(" prepend_size result:\n");
603 	//dump_buffer(buffer);
604 	return B_OK;
605 }
606 
607 
608 static status_t
609 prepend_data(net_buffer *buffer, const void *data, size_t size)
610 {
611 	void *contiguousBuffer;
612 	status_t status = prepend_size(buffer, size, &contiguousBuffer);
613 	if (status < B_OK)
614 		return status;
615 
616 	if (contiguousBuffer)
617 		memcpy(contiguousBuffer, data, size);
618 	else
619 		write_data(buffer, 0, data, size);
620 
621 	//dprintf(" prepend result:\n");
622 	//dump_buffer(buffer);
623 
624 	return B_OK;
625 }
626 
627 
628 static status_t
629 append_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
630 {
631 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
632 	data_node *node = (data_node *)list_get_last_item(&buffer->buffers);
633 
634 	TRACE(("append_size(buffer %p, size %ld)\n", buffer, size));
635 	//dump_buffer(buffer);
636 
637 	if (node->tail_space < size) {
638 		// we need to append a new buffer
639 
640 		// compute how many buffers we're going to need
641 		// TODO: this doesn't leave any tail space, if that should be desired...
642 		uint32 tailSpace = node->tail_space;
643 		uint32 minimalHeaderSpace = sizeof(data_header) + 2 * sizeof(data_node);
644 		uint32 sizeNeeded = size - tailSpace;
645 		uint32 count = (sizeNeeded + BUFFER_SIZE - minimalHeaderSpace - 1)
646 			/ (BUFFER_SIZE - minimalHeaderSpace);
647 		uint32 averageHeaderSpace = BUFFER_SIZE - sizeNeeded / count - sizeof(data_header);
648 		uint32 averageSize = BUFFER_SIZE - sizeof(data_header) - averageHeaderSpace;
649 
650 		// allocate space left in the node
651 		node->tail_space -= tailSpace;
652 		node->used += tailSpace;
653 		buffer->size += tailSpace;
654 
655 		// allocate all buffers
656 
657 		for (uint32 i = 0; i < count; i++) {
658 			data_header *header = create_data_header(BUFFER_SIZE, averageHeaderSpace);
659 			if (header == NULL) {
660 				// TODO: free up headers we already allocated!
661 				return B_NO_MEMORY;
662 			}
663 
664 			node = (data_node *)alloc_data_header_space(header, sizeof(data_node));
665 				// this can't fail as we made sure there will be enough header space
666 
667 			init_data_node(node, header, averageHeaderSpace);
668 			node->header_space = header->data_space;
669 			node->tail_space -= averageSize;
670 			node->used = averageSize;
671 			node->offset = buffer->size;
672 			buffer->size += averageSize;
673 
674 			list_add_item(&buffer->buffers, node);
675 		}
676 
677 		if (_contiguousBuffer)
678 			*_contiguousBuffer = NULL;
679 
680 		//dprintf(" append result 1:\n");
681 		//dump_buffer(buffer);
682 		return B_OK;
683 	}
684 
685 	// the data fits into this buffer
686 	node->tail_space -= size;
687 
688 	if (_contiguousBuffer)
689 		*_contiguousBuffer = node->start + node->used;
690 
691 	node->used += size;
692 	buffer->size += size;
693 
694 	//dprintf(" append result 2:\n");
695 	//dump_buffer(buffer);
696 	return B_OK;
697 }
698 
699 
700 static status_t
701 append_data(net_buffer *buffer, const void *data, size_t size)
702 {
703 	size_t used = buffer->size;
704 
705 	void *contiguousBuffer;
706 	status_t status = append_size(buffer, size, &contiguousBuffer);
707 	if (status < B_OK)
708 		return status;
709 
710 	if (contiguousBuffer)
711 		memcpy(contiguousBuffer, data, size);
712 	else
713 		write_data(buffer, used, data, size);
714 
715 	return B_OK;
716 }
717 
718 
719 /*!
720 	Removes bytes from the beginning of the buffer.
721 */
722 static status_t
723 remove_header(net_buffer *_buffer, size_t bytes)
724 {
725 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
726 
727 	if (bytes > buffer->size)
728 		return B_BAD_VALUE;
729 
730 	TRACE(("remove_header(buffer %p, %ld bytes)\n", buffer, bytes));
731 	//dump_buffer(buffer);
732 
733 	size_t left = bytes;
734 	data_node *node = NULL;
735 
736 	while (left >= 0) {
737 		node = (data_node *)list_get_first_item(&buffer->buffers);
738 		if (node == NULL) {
739 			if (left == 0)
740 				break;
741 			return B_ERROR;
742 		}
743 
744 		if (node->used > left)
745 			break;
746 
747 		// node will be removed completely
748 		list_remove_item(&buffer->buffers, node);
749 		left -= node->used;
750 		remove_data_node(node);
751 		node = NULL;
752 	}
753 
754 	// cut remaining node, if any
755 
756 	if (node != NULL) {
757 		size_t cut = min_c(node->used, left);
758 		node->offset = 0;
759 		node->start += cut;
760 		node->header_space += cut;
761 		node->used -= cut;
762 
763 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
764 	}
765 
766 	// adjust offset of following nodes
767 	while (node != NULL) {
768 		node->offset -= bytes;
769 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
770 	}
771 
772 	buffer->size -= bytes;
773 
774 	//dprintf(" remove result:\n");
775 	//dump_buffer(buffer);
776 	return B_OK;
777 }
778 
779 
780 /*!
781 	Removes bytes from the end of the buffer.
782 */
783 static status_t
784 remove_trailer(net_buffer *buffer, size_t bytes)
785 {
786 	return trim_data(buffer, buffer->size - bytes);
787 }
788 
789 
790 /*!
791 	Trims the buffer to the specified \a newSize by removing space from
792 	the end of the buffer.
793 */
794 static status_t
795 trim_data(net_buffer *_buffer, size_t newSize)
796 {
797 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
798 	TRACE(("trim_data(buffer %p, newSize = %ld, buffer size = %ld)\n",
799 		buffer, newSize, buffer->size));
800 	//dump_buffer(buffer);
801 
802 	if (newSize > buffer->size)
803 		return B_BAD_VALUE;
804 	if (newSize == buffer->size)
805 		return B_OK;
806 
807 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
808 	while (node->offset + node->used < newSize) {
809 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
810 		if (node == NULL) {
811 			// trim size greater than buffer size
812 			return B_BAD_VALUE;
813 		}
814 	}
815 
816 	int32 diff = node->used + node->offset - newSize;
817 	node->tail_space += diff;
818 	node->used -= diff;
819 
820 	if (node->used > 0)
821 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
822 
823 	while (node != NULL) {
824 		data_node *next = (data_node *)list_get_next_item(&buffer->buffers, node);
825 		list_remove_item(&buffer->buffers, node);
826 		remove_data_node(node);
827 
828 		node = next;
829 	}
830 
831 	buffer->size = newSize;
832 
833 	//dprintf(" trim result:\n");
834 	//dump_buffer(buffer);
835 	return B_OK;
836 }
837 
838 
839 /*!
840 	Tries to directly access the requested space in the buffer.
841 	If the space is contiguous, the function will succeed and place a pointer
842 	to that space into \a _contiguousBuffer.
843 
844 	\return B_BAD_VALUE if the offset is outside of the buffer's bounds.
845 	\return B_ERROR in case the buffer is not contiguous at that location.
846 */
847 static status_t
848 direct_access(net_buffer *_buffer, uint32 offset, size_t size,
849 	void **_contiguousBuffer)
850 {
851 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
852 
853 	//TRACE(("direct_access(buffer %p, offset %ld, size %ld)\n", buffer, offset, size));
854 
855 	if (offset + size > buffer->size)
856 		return B_BAD_VALUE;
857 
858 	// find node to access
859 
860 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
861 	while (node->offset + node->used < offset) {
862 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
863 		if (node == NULL)
864 			return B_BAD_VALUE;
865 	}
866 
867 	offset -= node->offset;
868 
869 	if (size > node->used - offset)
870 		return B_ERROR;
871 
872 	*_contiguousBuffer = node->start + offset;
873 	return B_OK;
874 }
875 
876 
877 static int32
878 checksum_data(net_buffer *_buffer, uint32 offset, size_t size, bool finalize)
879 {
880 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
881 
882 	if (offset + size > buffer->size || size == 0)
883 		return B_BAD_VALUE;
884 
885 	// find first node to read from
886 
887 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
888 	while (node->offset + node->used < offset) {
889 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
890 		if (node == NULL)
891 			return B_ERROR;
892 	}
893 
894 	offset -= node->offset;
895 
896 	// Since the maximum buffer size is 65536 bytes, it's impossible
897 	// to overlap 32 bit - we don't need to handle this overlap in
898 	// the loop, we can safely do it afterwards
899 	uint32 sum = 0;
900 
901 	while (true) {
902 		size_t bytes = min_c(size, node->used - offset);
903 		if ((offset + node->offset) & 1) {
904 			// if we're at an uneven offset, we have to swap the checksum
905 			sum += __swap_int16(compute_checksum(node->start + offset, bytes));
906 		} else
907 			sum += compute_checksum(node->start + offset, bytes);
908 
909 		size -= bytes;
910 		if (size == 0)
911 			break;
912 
913 		offset = 0;
914 
915 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
916 		if (node == NULL)
917 			return B_ERROR;
918 	}
919 
920 	while (sum >> 16) {
921 		sum = (sum & 0xffff) + (sum >> 16);
922 	}
923 
924 	if (!finalize)
925 		return (uint16)sum;
926 
927 	return (uint16)~sum;
928 }
929 
930 
931 static uint32
932 get_iovecs(net_buffer *_buffer, struct iovec *iovecs, uint32 vecCount)
933 {
934 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
935 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
936 	uint32 count = 0;
937 
938 	while (count < vecCount) {
939 		iovecs[count].iov_base = node->start;
940 		iovecs[count].iov_len = node->used;
941 		count++;
942 
943 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
944 		if (node == NULL)
945 			break;
946 	}
947 
948 	return count;
949 }
950 
951 
952 static uint32
953 count_iovecs(net_buffer *_buffer)
954 {
955 	net_buffer_private *buffer = (net_buffer_private *)_buffer;
956 	data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
957 	uint32 count = 0;
958 
959 	while (true) {
960 		count++;
961 
962 		node = (data_node *)list_get_next_item(&buffer->buffers, node);
963 		if (node == NULL)
964 			break;
965 	}
966 
967 	return count;
968 }
969 
970 
971 static status_t
972 std_ops(int32 op, ...)
973 {
974 	switch (op) {
975 		case B_MODULE_INIT:
976 		case B_MODULE_UNINIT:
977 			return B_OK;
978 
979 		default:
980 			return B_ERROR;
981 	}
982 }
983 
984 
985 net_buffer_module_info gNetBufferModule = {
986 	{
987 		NET_BUFFER_MODULE_NAME,
988 		0,
989 		std_ops
990 	},
991 	create_buffer,
992 	free_buffer,
993 
994 	duplicate_buffer,
995 	clone_buffer,
996 	split_buffer,
997 	merge_buffer,
998 
999 	prepend_size,
1000 	prepend_data,
1001 	append_size,
1002 	append_data,
1003 	NULL,	// insert
1004 	NULL,	// remove
1005 	remove_header,
1006 	remove_trailer,
1007 	trim_data,
1008 
1009 	NULL,	// associate_data
1010 
1011 	direct_access,
1012 	read_data,
1013 	write_data,
1014 
1015 	checksum_data,
1016 
1017 	NULL,	// get_memory_map
1018 	get_iovecs,
1019 	count_iovecs,
1020 
1021 	NULL,	// dump
1022 };
1023 
1024