1/*
2 * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
3 *
4 * All rights reserved. Distributed under the terms of the MIT License.
5 *
6 */
7
8
9#include "snet_buffer.h"
10
11#include <malloc.h>
12#include <string.h>
13#include <KernelExport.h>
14
15
16struct snet_buffer {
17	struct list_link	link;
18
19	uint8*				buffer;
20
21	uint16				allocatedSize;
22	uint16				expectedSize;
23	uint16				puttingSize;
24	uint16				pullingSize;
25
26	void*				cookie;
27};
28
29
30snet_buffer*
31snb_create(uint16 size)
32{
33	// TODO: pointer checking
34
35#ifdef SNB_BUFFER_ATTACHED
36	// Allocating these 2 buffers together might prevent memory fragmentation
37	snet_buffer* snb = (snet_buffer*) malloc(sizeof(snet_buffer) + size);
38	snb->buffer = ((uint8*)snb) + sizeof(snet_buffer);
39#else
40	snet_buffer* snb = malloc(sizeof (snet_buffer));
41	snb->buffer = malloc(size);
42#endif
43
44	snb->pullingSize = snb->puttingSize = 0;
45	snb->expectedSize = snb->allocatedSize = size;
46
47	return snb;
48}
49
50
51void
52snb_put(snet_buffer* snb, void* data, uint16 size)
53{
54	// TODO: check overflow
55	memcpy( &snb->buffer[snb->puttingSize], data, size);
56	snb->puttingSize+=size;
57}
58
59
60void*
61snb_pull(snet_buffer* snb, uint16 size)
62{
63	// TODO: check overflow
64	snb->pullingSize+=size;
65	return &snb->buffer[snb->pullingSize - size];
66
67}
68
69
70void
71snb_reset(snet_buffer* snb)
72{
73	snb->puttingSize = snb->pullingSize = 0;
74}
75
76
77void
78snb_free(snet_buffer* snb)
79{
80	if (snb == NULL)
81		return;
82
83#ifdef SNB_BUFFER_ATTACHED
84	free(snb);
85#else
86	free(snb->buffer);
87	free(snb);
88#endif
89
90}
91
92
93void*
94snb_get(snet_buffer* snb)
95{
96	// TODO: pointer checking
97	return snb->buffer;
98}
99
100
101uint16
102snb_size(snet_buffer* snb)
103{
104	// TODO: pointer checking
105	return snb->expectedSize;
106}
107
108
109void*
110snb_cookie(snet_buffer* snb)
111{
112	// TODO: pointer checking
113	return snb->cookie;
114}
115
116
117void
118snb_set_cookie(snet_buffer* snb, void* cookie)
119{
120	// TODO: pointer checking
121	snb->cookie = cookie;
122}
123
124
125// Return true if we canot "put" more data in the buffer
126bool
127snb_completed(snet_buffer* snb)
128{
129	return (snb->expectedSize == snb->puttingSize);
130}
131
132
133// Return true if we cannot pull more more data from the buffer
134bool
135snb_finished(snet_buffer* snb)
136{
137	return (snb->expectedSize == snb->pullingSize);
138}
139
140
141uint16
142snb_remaining_to_put(snet_buffer* snb)
143{
144	return (snb->expectedSize - snb->puttingSize);
145}
146
147
148uint16
149snb_remaining_to_pull(snet_buffer* snb)
150{
151	return (snb->expectedSize - snb->pullingSize);
152}
153
154/*
155	ISSUE1: Number of packets in the worst case(we always need a bigger
156	buffer than before) increases, never decreases:
157
158	SOL1: Delete the smallest when the queue is bigger than X elements
159	SOL2: ?
160
161	ISSUE2: If the queue is not gonna be used for long time. Memory c
162	ould be freed
163
164	SOL1: Provide purge func.
165	SOL2: ?
166*/
167
168
169static snet_buffer*
170snb_attempt_reuse(snet_buffer* snb, uint16 size)
171{
172	if (snb == NULL || (snb->allocatedSize < size)) {
173
174		// Impossible or not worth, Creating a new one
175		snb_free(snb);
176		return snb_create(size);
177
178	} else {
179		snb_reset(snb);
180		snb->expectedSize = size;
181		return snb;
182	}
183
184}
185
186
187void
188snb_park(struct list* l, snet_buffer* snb)
189{
190	snet_buffer* item = NULL;
191
192	// insert it by order
193	while ((item = (snet_buffer*)list_get_next_item(l, item)) != NULL) {
194		// This one has allocated more than us place us back
195		if (item->allocatedSize > snb->allocatedSize) {
196			list_insert_item_before(l, item, snb);
197			return;
198		}
199	}
200	// no buffer bigger than us(or empty).. then at the end
201	list_add_item(l, snb);
202}
203
204
205snet_buffer*
206snb_fetch(struct list* l, uint16 size)
207{
208	snet_buffer* item = NULL;
209	snet_buffer* newitem = NULL;
210
211	if (!list_is_empty(l))
212	while ((item = (snet_buffer*)list_get_next_item(l, item)) != NULL) {
213		if (item->allocatedSize >= size) {
214			// This one is for us
215			break;
216		}
217	}
218
219	newitem = snb_attempt_reuse(item, size);
220
221	/* the resulting reused one is the same
222	 * as we fetched? => remove it from list
223	 */
224	if (item == newitem) {
225		list_remove_item(l, item);
226	}
227
228	return newitem;
229}
230
231
232uint16
233snb_packets(struct list* l)
234{
235	uint16 count = 0;
236	snet_buffer* item = NULL;
237
238	while ((item = (snet_buffer*)list_get_next_item(l, item)) != NULL)
239		count++;
240
241	return count;
242}
243
244
245void
246snb_dump(snet_buffer* snb)
247{
248	kprintf("item=%p\tprev=%p\tnext=%p\tallocated=%d\n", snb, snb->link.prev,
249		snb->link.next, snb->allocatedSize);
250}
251