aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llbuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/llmessage/llbuffer.cpp765
1 files changed, 765 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llbuffer.cpp b/linden/indra/llmessage/llbuffer.cpp
new file mode 100644
index 0000000..07c730a
--- /dev/null
+++ b/linden/indra/llmessage/llbuffer.cpp
@@ -0,0 +1,765 @@
1/**
2 * @file llbuffer.cpp
3 * @author Phoenix
4 * @date 2005-09-20
5 * @brief Implementation of the segments, buffers, and buffer arrays.
6 *
7 * Copyright (c) 2005-2007, Linden Research, Inc.
8 *
9 * The source code in this file ("Source Code") is provided by Linden Lab
10 * to you under the terms of the GNU General Public License, version 2.0
11 * ("GPL"), unless you have obtained a separate licensing agreement
12 * ("Other License"), formally executed by you and Linden Lab. Terms of
13 * the GPL can be found in doc/GPL-license.txt in this distribution, or
14 * online at http://secondlife.com/developers/opensource/gplv2
15 *
16 * There are special exceptions to the terms and conditions of the GPL as
17 * it is applied to this Source Code. View the full text of the exception
18 * in the file doc/FLOSS-exception.txt in this software distribution, or
19 * online at http://secondlife.com/developers/opensource/flossexception
20 *
21 * By copying, modifying or distributing this software, you acknowledge
22 * that you have read and understood your obligations described above,
23 * and agree to abide by those obligations.
24 *
25 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
26 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
27 * COMPLETENESS OR PERFORMANCE.
28 */
29
30#include "linden_common.h"
31#include "llbuffer.h"
32
33#include "llmath.h"
34#include "llmemtype.h"
35#include "llstl.h"
36
37/**
38 * LLSegment
39 */
40LLSegment::LLSegment() :
41 mChannel(0),
42 mData(NULL),
43 mSize(0)
44{
45 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
46}
47
48LLSegment::LLSegment(S32 channel, U8* data, S32 data_len) :
49 mChannel(channel),
50 mData(data),
51 mSize(data_len)
52{
53 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
54}
55
56LLSegment::~LLSegment()
57{
58 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
59}
60
61bool LLSegment::isOnChannel(S32 channel) const
62{
63 return (mChannel == channel);
64}
65
66S32 LLSegment::getChannel() const
67{
68 return mChannel;
69}
70
71void LLSegment::setChannel(S32 channel)
72{
73 mChannel = channel;
74}
75
76
77U8* LLSegment::data() const
78{
79 return mData;
80}
81
82S32 LLSegment::size() const
83{
84 return mSize;
85}
86
87
88/**
89 * LLHeapBuffer
90 */
91LLHeapBuffer::LLHeapBuffer()
92{
93 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
94 const S32 DEFAULT_HEAP_BUFFER_SIZE = 16384;
95 allocate(DEFAULT_HEAP_BUFFER_SIZE);
96}
97
98LLHeapBuffer::LLHeapBuffer(S32 size)
99{
100 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
101 allocate(size);
102}
103
104LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len)
105{
106 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
107 if((len > 0) && src)
108 {
109 allocate(len);
110 if(mBuffer)
111 {
112 memcpy(mBuffer, src, len);
113 }
114 }
115 else
116 {
117 mBuffer = NULL;
118 mSize = 0;
119 mNextFree = NULL;
120 }
121}
122
123// virtual
124LLHeapBuffer::~LLHeapBuffer()
125{
126 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
127 delete[] mBuffer;
128 mBuffer = NULL;
129 mSize = 0;
130 mNextFree = NULL;
131}
132
133// virtual
134//S32 LLHeapBuffer::bytesLeft() const
135//{
136// return (mSize - (mNextFree - mBuffer));
137//}
138
139// virtual
140bool LLHeapBuffer::createSegment(
141 S32 channel,
142 S32 size,
143 LLSegment& segment)
144{
145 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
146 // get actual size of the segment.
147 S32 actual_size = llmin(size, (mSize - S32(mNextFree - mBuffer)));
148
149 // bail if we cannot build a valid segment
150 if(actual_size <= 0)
151 {
152 return false;
153 }
154
155 // Yay, we're done.
156 segment = LLSegment(channel, mNextFree, actual_size);
157 mNextFree += actual_size;
158 return true;
159}
160
161void LLHeapBuffer::allocate(S32 size)
162{
163 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
164 mBuffer = new U8[size];
165 if(mBuffer)
166 {
167 mSize = size;
168 mNextFree = mBuffer;
169 }
170}
171
172
173/**
174 * LLBufferArray
175 */
176LLBufferArray::LLBufferArray() :
177 mNextBaseChannel(0)
178{
179 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
180}
181
182LLBufferArray::~LLBufferArray()
183{
184 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
185 std::for_each(mBuffers.begin(), mBuffers.end(), DeletePointer());
186}
187
188// static
189LLChannelDescriptors LLBufferArray::makeChannelConsumer(
190 const LLChannelDescriptors& channels)
191{
192 LLChannelDescriptors rv(channels.out());
193 return rv;
194}
195
196LLChannelDescriptors LLBufferArray::nextChannel()
197{
198 LLChannelDescriptors rv(mNextBaseChannel++);
199 return rv;
200}
201
202bool LLBufferArray::append(S32 channel, const U8* src, S32 len)
203{
204 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
205 std::vector<LLSegment> segments;
206 if(copyIntoBuffers(channel, src, len, segments))
207 {
208 mSegments.insert(mSegments.end(), segments.begin(), segments.end());
209 return true;
210 }
211 return false;
212}
213
214bool LLBufferArray::prepend(S32 channel, const U8* src, S32 len)
215{
216 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
217 std::vector<LLSegment> segments;
218 if(copyIntoBuffers(channel, src, len, segments))
219 {
220 mSegments.insert(mSegments.begin(), segments.begin(), segments.end());
221 return true;
222 }
223 return false;
224}
225
226bool LLBufferArray::insertAfter(
227 segment_iterator_t segment,
228 S32 channel,
229 const U8* src,
230 S32 len)
231{
232 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
233 std::vector<LLSegment> segments;
234 if(mSegments.end() != segment)
235 {
236 ++segment;
237 }
238 if(copyIntoBuffers(channel, src, len, segments))
239 {
240 mSegments.insert(segment, segments.begin(), segments.end());
241 return true;
242 }
243 return false;
244}
245
246LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
247{
248 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
249 segment_iterator_t end = mSegments.end();
250 segment_iterator_t it = getSegment(address);
251 if(it == end)
252 {
253 return end;
254 }
255
256 // We have the location and the segment.
257 U8* base = (*it).data();
258 S32 size = (*it).size();
259 if(address == (base + size))
260 {
261 // No need to split, since this is the last byte of the
262 // segment. We do not want to have zero length segments, since
263 // that will only incur processing overhead with no advantage.
264 return it;
265 }
266 S32 channel = (*it).getChannel();
267 LLSegment segment1(channel, base, (address - base) + 1);
268 *it = segment1;
269 segment_iterator_t rv = it;
270 ++it;
271 LLSegment segment2(channel, address + 1, size - (address - base) - 1);
272 mSegments.insert(it, segment2);
273 return rv;
274}
275
276LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
277{
278 return mSegments.begin();
279}
280
281LLBufferArray::segment_iterator_t LLBufferArray::endSegment()
282{
283 return mSegments.end();
284}
285
286LLBufferArray::segment_iterator_t LLBufferArray::constructSegmentAfter(
287 U8* address,
288 LLSegment& segment)
289{
290 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
291 segment_iterator_t rv = mSegments.begin();
292 segment_iterator_t end = mSegments.end();
293 if(!address)
294 {
295 if(rv != end)
296 {
297 segment = (*rv);
298 }
299 }
300 else
301 {
302 // we have an address - find the segment it is in.
303 for( ; rv != end; ++rv)
304 {
305 if((address >= (*rv).data())
306 && (address < ((*rv).data() + (*rv).size())))
307 {
308 if((++address) < ((*rv).data() + (*rv).size()))
309 {
310 // it's in this segment - construct an appropriate
311 // sub-segment.
312 segment = LLSegment(
313 (*rv).getChannel(),
314 address,
315 (*rv).size() - (address - (*rv).data()));
316 }
317 else
318 {
319 ++rv;
320 if(rv != end)
321 {
322 segment = (*rv);
323 }
324 }
325 break;
326 }
327 }
328 }
329 if(rv == end)
330 {
331 segment = LLSegment();
332 }
333 return rv;
334}
335
336LLBufferArray::segment_iterator_t LLBufferArray::getSegment(U8* address)
337{
338 segment_iterator_t end = mSegments.end();
339 if(!address)
340 {
341 return end;
342 }
343 segment_iterator_t it = mSegments.begin();
344 for( ; it != end; ++it)
345 {
346 if((address >= (*it).data())&&(address < (*it).data() + (*it).size()))
347 {
348 // found it.
349 return it;
350 }
351 }
352 return end;
353}
354
355LLBufferArray::const_segment_iterator_t LLBufferArray::getSegment(
356 U8* address) const
357{
358 const_segment_iterator_t end = mSegments.end();
359 if(!address)
360 {
361 return end;
362 }
363 const_segment_iterator_t it = mSegments.begin();
364 for( ; it != end; ++it)
365 {
366 if((address >= (*it).data())
367 && (address < (*it).data() + (*it).size()))
368 {
369 // found it.
370 return it;
371 }
372 }
373 return end;
374}
375
376/*
377U8* LLBufferArray::getAddressAfter(U8* address)
378{
379 U8* rv = NULL;
380 segment_iterator_t it = getSegment(address);
381 segment_iterator_t end = mSegments.end();
382 if(it != end)
383 {
384 if(++address < ((*it).data() + (*it).size()))
385 {
386 // it's in the same segment
387 rv = address;
388 }
389 else
390 {
391 // it's in the next segment
392 if(++it != end)
393 {
394 rv = (*it).data();
395 }
396 }
397 }
398 return rv;
399}
400*/
401
402S32 LLBufferArray::countAfter(S32 channel, U8* start) const
403{
404 S32 count = 0;
405 S32 offset = 0;
406 const_segment_iterator_t it;
407 const_segment_iterator_t end = mSegments.end();
408 if(start)
409 {
410 it = getSegment(start);
411 if(it == end)
412 {
413 return count;
414 }
415 if(++start < ((*it).data() + (*it).size()))
416 {
417 // it's in the same segment
418 offset = start - (*it).data();
419 }
420 else if(++it == end)
421 {
422 // it's in the next segment
423 return count;
424 }
425 }
426 else
427 {
428 it = mSegments.begin();
429 }
430 while(it != end)
431 {
432 if((*it).isOnChannel(channel))
433 {
434 count += (*it).size() - offset;
435 }
436 offset = 0;
437 ++it;
438 }
439 return count;
440}
441
442U8* LLBufferArray::readAfter(
443 S32 channel,
444 U8* start,
445 U8* dest,
446 S32& len) const
447{
448 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
449 U8* rv = start;
450 if(!dest || len <= 0)
451 {
452 return rv;
453 }
454 S32 bytes_left = len;
455 len = 0;
456 S32 bytes_to_copy = 0;
457 const_segment_iterator_t it;
458 const_segment_iterator_t end = mSegments.end();
459 if(start)
460 {
461 it = getSegment(start);
462 if(it == end)
463 {
464 return rv;
465 }
466 if((++start < ((*it).data() + (*it).size()))
467 && (*it).isOnChannel(channel))
468 {
469 // copy the data out of this segment
470 S32 bytes_in_segment = (*it).size() - (start - (*it).data());
471 bytes_to_copy = llmin(bytes_left, bytes_in_segment);
472 memcpy(dest, start, bytes_to_copy); /*Flawfinder: ignore*/
473 len += bytes_to_copy;
474 bytes_left -= bytes_to_copy;
475 rv = start + bytes_to_copy - 1;
476 ++it;
477 }
478 else
479 {
480 ++it;
481 }
482 }
483 else
484 {
485 it = mSegments.begin();
486 }
487 while(bytes_left && (it != end))
488 {
489 if(!((*it).isOnChannel(channel)))
490 {
491 ++it;
492 continue;
493 }
494 bytes_to_copy = llmin(bytes_left, (*it).size());
495 memcpy(dest + len, (*it).data(), bytes_to_copy); /*Flawfinder: ignore*/
496 len += bytes_to_copy;
497 bytes_left -= bytes_to_copy;
498 rv = (*it).data() + bytes_to_copy - 1;
499 ++it;
500 }
501 return rv;
502}
503
504U8* LLBufferArray::seek(
505 S32 channel,
506 U8* start,
507 S32 delta) const
508{
509 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
510 const_segment_iterator_t it;
511 const_segment_iterator_t end = mSegments.end();
512 U8* rv = start;
513 if(0 == delta)
514 {
515 if((U8*)npos == start)
516 {
517 // someone is looking for end of data.
518 segment_list_t::const_reverse_iterator rit = mSegments.rbegin();
519 segment_list_t::const_reverse_iterator rend = mSegments.rend();
520 while(rit != rend)
521 {
522 if(!((*rit).isOnChannel(channel)))
523 {
524 ++rit;
525 continue;
526 }
527 rv = (*rit).data() + (*rit).size();
528 break;
529 }
530 }
531 else if(start)
532 {
533 // This is sort of a weird case - check if zero bytes away
534 // from current position is on channel and return start if
535 // that is true. Otherwise, return NULL.
536 it = getSegment(start);
537 if((it == end) || !(*it).isOnChannel(channel))
538 {
539 rv = NULL;
540 }
541 }
542 else
543 {
544 // Start is NULL, so return the very first byte on the
545 // channel, or NULL.
546 it = mSegments.begin();
547 while((it != end) && !(*it).isOnChannel(channel))
548 {
549 ++it;
550 }
551 if(it != end)
552 {
553 rv = (*it).data();
554 }
555 }
556 return rv;
557 }
558 if(start)
559 {
560 it = getSegment(start);
561 if((it != end) && (*it).isOnChannel(channel))
562 {
563 if(delta > 0)
564 {
565 S32 bytes_in_segment = (*it).size() - (start - (*it).data());
566 S32 local_delta = llmin(delta, bytes_in_segment);
567 rv += local_delta;
568 delta -= local_delta;
569 ++it;
570 }
571 else
572 {
573 S32 bytes_in_segment = start - (*it).data();
574 S32 local_delta = llmin(llabs(delta), bytes_in_segment);
575 rv -= local_delta;
576 delta += local_delta;
577 }
578 }
579 }
580 else if(delta < 0)
581 {
582 // start is NULL, and delta indicates seeking backwards -
583 // return NULL.
584 return NULL;
585 }
586 else
587 {
588 // start is NULL and delta > 0
589 it = mSegments.begin();
590 }
591 if(delta > 0)
592 {
593 // At this point, we have an iterator into the segments, and
594 // are seeking forward until delta is zero or we run out
595 while(delta && (it != end))
596 {
597 if(!((*it).isOnChannel(channel)))
598 {
599 ++it;
600 continue;
601 }
602 if(delta <= (*it).size())
603 {
604 // it's in this segment
605 rv = (*it).data() + delta;
606 }
607 delta -= (*it).size();
608 ++it;
609 }
610 if(delta && (it == end))
611 {
612 // Whoops - sought past end.
613 rv = NULL;
614 }
615 }
616 else //if(delta < 0)
617 {
618 // We are at the beginning of a segment, and need to search
619 // backwards.
620 segment_list_t::const_reverse_iterator rit(it);
621 segment_list_t::const_reverse_iterator rend = mSegments.rend();
622 while(delta && (rit != rend))
623 {
624 if(!((*rit).isOnChannel(channel)))
625 {
626 ++rit;
627 continue;
628 }
629 if(llabs(delta) <= (*rit).size())
630 {
631 // it's in this segment.
632 rv = (*rit).data() + (*rit).size() + delta;
633 delta = 0;
634 }
635 else
636 {
637 delta += (*rit).size();
638 }
639 ++rit;
640 }
641 if(delta && (rit == rend))
642 {
643 // sought past the beginning.
644 rv = NULL;
645 }
646 }
647 return rv;
648}
649
650bool LLBufferArray::takeContents(LLBufferArray& source)
651{
652 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
653 std::copy(
654 source.mBuffers.begin(),
655 source.mBuffers.end(),
656 std::back_insert_iterator<buffer_list_t>(mBuffers));
657 source.mBuffers.clear();
658 std::copy(
659 source.mSegments.begin(),
660 source.mSegments.end(),
661 std::back_insert_iterator<segment_list_t>(mSegments));
662 source.mSegments.clear();
663 source.mNextBaseChannel = 0;
664 return true;
665}
666
667LLBufferArray::segment_iterator_t LLBufferArray::makeSegment(
668 S32 channel,
669 S32 len)
670{
671 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
672 // start at the end of the buffers, because it is the most likely
673 // to have free space.
674 LLSegment segment;
675 buffer_list_t::reverse_iterator it = mBuffers.rbegin();
676 buffer_list_t::reverse_iterator end = mBuffers.rend();
677 bool made_segment = false;
678 for(; it != end; ++it)
679 {
680 if((*it)->createSegment(channel, len, segment))
681 {
682 made_segment = true;
683 break;
684 }
685 }
686 segment_iterator_t send = mSegments.end();
687 if(!made_segment)
688 {
689 LLBuffer* buf = new LLHeapBuffer;
690 mBuffers.push_back(buf);
691 if(!buf->createSegment(channel, len, segment))
692 {
693 // failed. this should never happen.
694 return send;
695 }
696 }
697
698 // store and return the newly made segment
699 mSegments.insert(send, segment);
700 std::list<LLSegment>::reverse_iterator rv = mSegments.rbegin();
701 ++rv;
702 send = rv.base();
703 return send;
704}
705
706bool LLBufferArray::eraseSegment(const segment_iterator_t& iter)
707{
708 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
709 // *FIX: in theory, we could reclaim the memory. We are leaking a
710 // bit of buffered memory into an unusable but still referenced
711 // location.
712 (void)mSegments.erase(iter);
713 return true;
714}
715
716
717bool LLBufferArray::copyIntoBuffers(
718 S32 channel,
719 const U8* src,
720 S32 len,
721 std::vector<LLSegment>& segments)
722{
723 LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
724 if(!src || !len) return false;
725 S32 copied = 0;
726 LLSegment segment;
727 buffer_iterator_t it = mBuffers.begin();
728 buffer_iterator_t end = mBuffers.end();
729 for(; it != end;)
730 {
731 if(!(*it)->createSegment(channel, len, segment))
732 {
733 ++it;
734 continue;
735 }
736 segments.push_back(segment);
737 S32 bytes = llmin(segment.size(), len);
738 memcpy(segment.data(), src + copied, bytes); /* Flawfinder Ignore */
739 copied += bytes;
740 len -= bytes;
741 if(0 == len)
742 {
743 break;
744 }
745 }
746 while(len)
747 {
748 LLBuffer* buf = new LLHeapBuffer;
749 mBuffers.push_back(buf);
750 if(!buf->createSegment(channel, len, segment))
751 {
752 // this totally failed - bail. This is the weird corner
753 // case were we 'leak' memory. No worries about an actual
754 // leak - we will still reclaim the memory later, but this
755 // particular buffer array is hosed for some reason.
756 // This should never happen.
757 return false;
758 }
759 segments.push_back(segment);
760 memcpy(segment.data(), src + copied, segment.size());
761 copied += segment.size();
762 len -= segment.size();
763 }
764 return true;
765}