1 /* 2 * Copyright 2006-2007, 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 "BufferQueue.h" 11 12 #include <KernelExport.h> 13 14 15 //#define TRACE_BUFFER_QUEUE 16 #ifdef TRACE_BUFFER_QUEUE 17 # define TRACE(x) dprintf x 18 #else 19 # define TRACE(x) 20 #endif 21 22 23 BufferQueue::BufferQueue(size_t maxBytes) 24 : 25 fMaxBytes(maxBytes), 26 fNumBytes(0), 27 fContiguousBytes(0), 28 fFirstSequence(0), 29 fLastSequence(0), 30 fPushPointer(0) 31 { 32 } 33 34 35 BufferQueue::~BufferQueue() 36 { 37 // free up any buffers left in the queue 38 39 net_buffer *buffer; 40 while ((buffer = fList.RemoveHead()) != NULL) { 41 gBufferModule->free(buffer); 42 } 43 } 44 45 46 void 47 BufferQueue::SetMaxBytes(size_t maxBytes) 48 { 49 fMaxBytes = maxBytes; 50 } 51 52 53 void 54 BufferQueue::SetInitialSequence(tcp_sequence sequence) 55 { 56 TRACE(("BufferQueue@%p::SetInitialSequence(%lu)\n", this, (uint32)sequence)); 57 58 fFirstSequence = fLastSequence = sequence; 59 } 60 61 62 63 void 64 BufferQueue::Add(net_buffer *buffer) 65 { 66 Add(buffer, fLastSequence); 67 } 68 69 70 void 71 BufferQueue::Add(net_buffer *buffer, tcp_sequence sequence) 72 { 73 TRACE(("BufferQueue@%p::Add(buffer %p, size %lu, sequence %lu)\n", 74 this, buffer, buffer->size, (uint32)sequence)); 75 TRACE((" in: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes)); 76 77 buffer->sequence = sequence; 78 79 if (fList.IsEmpty() || sequence >= fLastSequence) { 80 // we usually just add the buffer to the end of the queue 81 fList.Add(buffer); 82 83 if (sequence == fLastSequence && fLastSequence - fFirstSequence == fNumBytes) { 84 // there is no hole in the buffer, we can make the whole buffer available 85 fContiguousBytes += buffer->size; 86 } 87 88 fLastSequence = sequence + buffer->size; 89 fNumBytes += buffer->size; 90 TRACE((" out0: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes)); 91 return; 92 } 93 94 if (fLastSequence < sequence + buffer->size) 95 fLastSequence = sequence + buffer->size; 96 97 if (fFirstSequence > sequence) { 98 // this buffer contains data that is already long gone - trim it 99 gBufferModule->remove_header(buffer, fFirstSequence - sequence); 100 sequence = fFirstSequence; 101 } 102 103 // find for the place where to insert the buffer into the queue 104 105 SegmentList::ReverseIterator iterator = fList.GetReverseIterator(); 106 net_buffer *previous = NULL; 107 net_buffer *next = NULL; 108 while ((previous = iterator.Next()) != NULL) { 109 if (sequence >= previous->sequence) { 110 // The new fragment can be inserted after this one 111 break; 112 } 113 114 next = previous; 115 } 116 117 // check if we have duplicate data, and remove it if that is the case 118 if (previous != NULL) { 119 if (sequence == previous->sequence) { 120 // we already have at least part of this data - ignore new data whenever 121 // it makes sense (because some TCP implementations send bogus data when 122 // probing the window) 123 if (previous->size >= buffer->size) { 124 gBufferModule->free(buffer); 125 buffer = NULL; 126 } else { 127 fList.Remove(previous); 128 gBufferModule->free(previous); 129 } 130 } else if (tcp_sequence(previous->sequence + previous->size) > sequence) 131 gBufferModule->remove_header(buffer, previous->sequence + previous->size - sequence); 132 } 133 134 if (buffer != NULL && next != NULL 135 && tcp_sequence(sequence + buffer->size) > next->sequence) { 136 // we already have at least part of this data 137 if (tcp_sequence(next->sequence + next->size) < sequence + buffer->size) { 138 net_buffer *remove = next; 139 next = (net_buffer *)next->link.next; 140 141 fList.Remove(remove); 142 gBufferModule->free(remove); 143 } else 144 gBufferModule->remove_trailer(buffer, next->sequence - (sequence + buffer->size)); 145 } 146 147 if (buffer == NULL) { 148 TRACE((" out1: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes)); 149 return; 150 } 151 152 fList.Insert(next, buffer); 153 fNumBytes += buffer->size; 154 155 // we might need to update the number of bytes available 156 157 if (fLastSequence - fFirstSequence == fNumBytes) 158 fContiguousBytes = fNumBytes; 159 else if (fFirstSequence + fContiguousBytes == sequence) { 160 // the complicated case: the new segment may have connected almost all 161 // buffers in the queue (but not all, or the above would be true) 162 163 do { 164 fContiguousBytes += buffer->size; 165 166 buffer = (struct net_buffer *)buffer->link.next; 167 } while (buffer != NULL && fFirstSequence + fContiguousBytes == buffer->sequence); 168 } 169 170 TRACE((" out2: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes)); 171 } 172 173 174 /*! 175 Removes all data in the queue up to the \a sequence number as specified. 176 177 NOTE: 178 If there are missing segments in the buffers to be removed, 179 fContiguousBytes is not maintained correctly! 180 */ 181 status_t 182 BufferQueue::RemoveUntil(tcp_sequence sequence) 183 { 184 TRACE(("BufferQueue@%p::RemoveUntil(sequence %lu)\n", this, (uint32)sequence)); 185 186 if (sequence < fFirstSequence) 187 return B_OK; 188 189 SegmentList::Iterator iterator = fList.GetIterator(); 190 net_buffer *buffer = NULL; 191 while ((buffer = iterator.Next()) != NULL && buffer->sequence < sequence) { 192 if (sequence >= buffer->sequence + buffer->size) { 193 // remove this buffer completely 194 iterator.Remove(); 195 fNumBytes -= buffer->size; 196 197 fContiguousBytes -= buffer->size; 198 gBufferModule->free(buffer); 199 } else { 200 // remove the header as far as needed 201 size_t size = sequence - buffer->sequence; 202 gBufferModule->remove_header(buffer, size); 203 204 buffer->sequence += size; 205 fNumBytes -= size; 206 fContiguousBytes -= size; 207 break; 208 } 209 } 210 211 if (fList.IsEmpty()) 212 fFirstSequence = fLastSequence; 213 else 214 fFirstSequence = fList.Head()->sequence; 215 216 return B_OK; 217 } 218 219 220 /*! 221 Clones the requested data in the buffer queue into the provided \a buffer. 222 */ 223 status_t 224 BufferQueue::Get(net_buffer *buffer, tcp_sequence sequence, size_t bytes) 225 { 226 TRACE(("BufferQueue@%p::Get(sequence %lu, bytes %lu)\n", this, (uint32)sequence, bytes)); 227 228 if (bytes == 0) 229 return B_OK; 230 231 if (sequence >= fLastSequence || sequence < fFirstSequence) { 232 // we don't have the requested data 233 return B_BAD_VALUE; 234 } 235 if (tcp_sequence(sequence + bytes) > fLastSequence) 236 bytes = fLastSequence - sequence; 237 238 size_t bytesLeft = bytes; 239 240 // find first buffer matching the sequence 241 242 SegmentList::Iterator iterator = fList.GetIterator(); 243 net_buffer *source = NULL; 244 while ((source = iterator.Next()) != NULL) { 245 if (sequence < source->sequence + source->size) 246 break; 247 } 248 249 if (source == NULL) 250 panic("we should have had that data..."); 251 if (source->sequence > sequence) 252 panic("source %p, sequence = %lu (%lu)\n", source, source->sequence, (uint32)sequence); 253 254 // clone the data 255 256 uint32 offset = sequence - source->sequence; 257 258 while (source != NULL && bytesLeft > 0) { 259 size_t size = min_c(source->size - offset, bytesLeft); 260 status_t status = gBufferModule->append_cloned(buffer, source, offset, size); 261 if (status < B_OK) 262 return status; 263 264 bytesLeft -= size; 265 offset = 0; 266 source = iterator.Next(); 267 } 268 269 return B_OK; 270 } 271 272 273 /*! 274 Creates a new buffer containing \a bytes bytes from the start of the 275 buffer queue. If \a remove is \c true, the data is removed from the 276 queue, if not, the data is cloned from the queue. 277 */ 278 status_t 279 BufferQueue::Get(size_t bytes, bool remove, net_buffer **_buffer) 280 { 281 if (bytes > Available()) 282 bytes = Available(); 283 284 if (bytes == 0) { 285 // we don't need to create a buffer when there is no data 286 *_buffer = NULL; 287 return B_OK; 288 } 289 290 net_buffer *buffer = fList.First(); 291 size_t bytesLeft = bytes; 292 ASSERT(buffer != NULL); 293 294 if (!remove || buffer->size > bytes) { 295 // we need a new buffer 296 buffer = gBufferModule->create(256); 297 if (buffer == NULL) 298 return B_NO_MEMORY; 299 } else { 300 // we can reuse this buffer 301 bytesLeft -= buffer->size; 302 fFirstSequence += buffer->size; 303 304 fList.Remove(buffer); 305 } 306 307 // clone/copy the remaining data 308 309 SegmentList::Iterator iterator = fList.GetIterator(); 310 net_buffer *source = NULL; 311 status_t status = B_OK; 312 while (bytesLeft > 0 && (source = iterator.Next()) != NULL) { 313 size_t size = min_c(source->size, bytesLeft); 314 status_t status = gBufferModule->append_cloned(buffer, source, 0, size); 315 if (status < B_OK) 316 break; 317 318 bytesLeft -= size; 319 320 if (!remove) 321 continue; 322 323 // remove either the whole buffer or only the part we cloned 324 325 fFirstSequence += size; 326 327 if (size == source->size) { 328 iterator.Remove(); 329 gBufferModule->free(source); 330 } else { 331 gBufferModule->remove_header(source, size); 332 source->sequence += size; 333 } 334 } 335 336 if (status == B_OK) { 337 *_buffer = buffer; 338 if (remove) { 339 fNumBytes -= bytes; 340 fContiguousBytes -= bytes; 341 } 342 } else 343 gBufferModule->free(buffer); 344 345 return status; 346 } 347 348 349 size_t 350 BufferQueue::Available(tcp_sequence sequence) const 351 { 352 if (sequence > (uint32)fFirstSequence + fContiguousBytes) 353 return 0; 354 355 return fContiguousBytes + fFirstSequence - sequence; 356 } 357 358 void 359 BufferQueue::SetPushPointer() 360 { 361 if (fList.IsEmpty()) 362 fPushPointer = 0; 363 else 364 fPushPointer = fList.Tail()->sequence + fList.Tail()->size; 365 } 366