/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2005,2006,2007 INRIA * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage */ #include "buffer.h" #include "ns3/assert.h" #include "ns3/debug.h" #include NS_DEBUG_COMPONENT_DEFINE ("Buffer"); #define DEBUG_INTERNAL_STATE(y) \ NS_DEBUG (y << "start="<m_count > 1 && m_end < m_data->m_dirtyEnd; if (GetInternalEnd () + end <= m_data->m_size && !isDirty) { /* enough space in buffer and not dirty * Add: |...| * Before: |**----*****| * After: |**----...**| */ NS_ASSERT (m_data->m_count == 1 || m_end == m_data->m_dirtyEnd); m_end += end; g_nAddNoRealloc++; } #if 0 // this is an optimization else if (GetInternalEnd () + end > m_data->m_size) { struct BufferData *newData = Buffer::Create (newSize); memcpy (newData->m_data + m_start, m_data->m_data + m_start, GetInternalSize ()); m_data->m_count--; if (m_data->m_count == 0) { Buffer::Recycle (m_data); } m_data = newData; m_end += end; } #endif else { uint32_t newSize = GetInternalSize () + end; struct BufferData *newData = Buffer::Create (newSize); memcpy (newData->m_data, m_data->m_data + m_start, GetInternalSize ()); m_data->m_count--; if (m_data->m_count == 0) { Buffer::Recycle (m_data); } m_data = newData; m_zeroAreaStart -= m_start; m_zeroAreaEnd -= m_start; m_end -= m_start; m_start = 0; m_end += end; g_nAddRealloc++; } m_maxZeroAreaStart = std::max (m_maxZeroAreaStart, m_zeroAreaStart); // update dirty area m_data->m_dirtyStart = m_start; m_data->m_dirtyEnd = m_end; DEBUG_INTERNAL_STATE ("add end=" << end << ", "); NS_ASSERT (CheckInternalState ()); } void Buffer::RemoveAtStart (uint32_t start) { NS_ASSERT (CheckInternalState ()); uint32_t newStart = m_start + start; if (newStart <= m_zeroAreaStart) { /* only remove start of buffer */ m_start = newStart; } else if (newStart <= m_zeroAreaEnd) { /* remove start of buffer _and_ start of zero area */ uint32_t delta = newStart - m_zeroAreaStart; m_start = m_zeroAreaStart; m_zeroAreaEnd -= delta; m_end -= delta; } else if (newStart <= m_end) { /* remove start of buffer, complete zero area, and part * of end of buffer */ NS_ASSERT (m_end >= start); uint32_t zeroSize = m_zeroAreaEnd - m_zeroAreaStart; m_start = newStart - zeroSize; m_end -= zeroSize; m_zeroAreaStart = m_start; m_zeroAreaEnd = m_start; } else { /* remove all buffer */ m_end -= m_zeroAreaEnd - m_zeroAreaStart; m_start = m_end; m_zeroAreaEnd = m_end; m_zeroAreaStart = m_end; } m_maxZeroAreaStart = std::max (m_maxZeroAreaStart, m_zeroAreaStart); DEBUG_INTERNAL_STATE ("rem start=" << start << ", "); NS_ASSERT (CheckInternalState ()); } void Buffer::RemoveAtEnd (uint32_t end) { NS_ASSERT (CheckInternalState ()); uint32_t newEnd = m_end - std::min (end, m_end - m_start); if (newEnd > m_zeroAreaEnd) { /* remove part of end of buffer */ m_end = newEnd; } else if (newEnd > m_zeroAreaStart) { /* remove end of buffer, part of zero area */ m_end = newEnd; m_zeroAreaEnd = newEnd; } else if (newEnd > m_start) { /* remove end of buffer, zero area, part of start of buffer */ m_end = newEnd; m_zeroAreaEnd = newEnd; m_zeroAreaStart = newEnd; } else { /* remove all buffer */ m_end = m_start; m_zeroAreaEnd = m_start; m_zeroAreaStart = m_start; } m_maxZeroAreaStart = std::max (m_maxZeroAreaStart, m_zeroAreaStart); DEBUG_INTERNAL_STATE ("rem end=" << end << ", "); NS_ASSERT (CheckInternalState ()); } Buffer Buffer::CreateFragment (uint32_t start, uint32_t length) const { NS_ASSERT (CheckInternalState ()); uint32_t zeroStart = m_zeroAreaStart - m_start; uint32_t zeroEnd = zeroStart + m_zeroAreaEnd; if (m_zeroAreaEnd != 0 && start + length > zeroStart && start <= zeroEnd) { TransformIntoRealBuffer (); } Buffer tmp = *this; tmp.RemoveAtStart (start); tmp.RemoveAtEnd (GetSize () - (start + length)); NS_ASSERT (CheckInternalState ()); return tmp; } Buffer Buffer::CreateFullCopy (void) const { NS_ASSERT (CheckInternalState ()); if (m_zeroAreaEnd != 0) { Buffer tmp; tmp.AddAtStart (m_zeroAreaEnd - m_zeroAreaStart); tmp.Begin ().WriteU8 (0, m_zeroAreaEnd - m_zeroAreaStart); uint32_t dataStart = m_zeroAreaStart - m_start; tmp.AddAtStart (dataStart); tmp.Begin ().Write (m_data->m_data+m_start, dataStart); uint32_t dataEnd = m_end - m_zeroAreaEnd; tmp.AddAtEnd (dataEnd); Buffer::Iterator i = tmp.End (); i.Prev (dataEnd); i.Write (m_data->m_data+m_zeroAreaStart,dataEnd); return tmp; } NS_ASSERT (CheckInternalState ()); return *this; } void Buffer::TransformIntoRealBuffer (void) const { NS_ASSERT (CheckInternalState ()); Buffer tmp = CreateFullCopy (); *const_cast (this) = tmp; NS_ASSERT (CheckInternalState ()); } uint8_t const* Buffer::PeekData (void) const { NS_ASSERT (CheckInternalState ()); TransformIntoRealBuffer (); NS_ASSERT (CheckInternalState ()); return m_data->m_data + m_start; } } // namespace ns3 #ifdef RUN_SELF_TESTS #include "ns3/test.h" #include "ns3/random-variable.h" #include namespace ns3 { class BufferTest: public Test { private: bool EnsureWrittenBytes (Buffer b, uint32_t n, uint8_t array[]); public: virtual bool RunTests (void); BufferTest (); }; BufferTest::BufferTest () : Test ("Buffer") {} bool BufferTest::EnsureWrittenBytes (Buffer b, uint32_t n, uint8_t array[]) { bool success = true; uint8_t *expected = array; uint8_t const*got; got = b.PeekData (); for (uint32_t j = 0; j < n; j++) { if (got[j] != expected[j]) { success = false; } } if (!success) { Failure () << "Buffer -- "; Failure () << "expected: n="; Failure () << n << ", "; Failure ().setf (std::ios::hex, std::ios::basefield); for (uint32_t j = 0; j < n; j++) { Failure () << (uint16_t)expected[j] << " "; } Failure ().setf (std::ios::dec, std::ios::basefield); Failure () << "got: "; Failure ().setf (std::ios::hex, std::ios::basefield); for (uint32_t j = 0; j < n; j++) { Failure () << (uint16_t)got[j] << " "; } Failure () << std::endl; } return success; } /* Note: works only when variadic macros are * available which is the case for gcc. * XXX */ #define ENSURE_WRITTEN_BYTES(buffer, n, ...) \ { \ uint8_t bytes[] = {__VA_ARGS__}; \ if (!EnsureWrittenBytes (buffer, n , bytes)) \ { \ result = false; \ } \ } bool BufferTest::RunTests (void) { bool result = true; Buffer buffer; Buffer::Iterator i; buffer.AddAtStart (6); i = buffer.Begin (); i.WriteU8 (0x66); ENSURE_WRITTEN_BYTES (buffer, 1, 0x66); i = buffer.Begin (); i.WriteU8 (0x67); ENSURE_WRITTEN_BYTES (buffer, 1, 0x67); i.WriteHtonU16 (0x6568); i = buffer.Begin (); ENSURE_WRITTEN_BYTES (buffer, 3, 0x67, 0x65, 0x68); i.WriteHtonU16 (0x6369); ENSURE_WRITTEN_BYTES (buffer, 3, 0x63, 0x69, 0x68); i.WriteHtonU32 (0xdeadbeaf); ENSURE_WRITTEN_BYTES (buffer, 6, 0x63, 0x69, 0xde, 0xad, 0xbe, 0xaf); buffer.AddAtStart (2); i = buffer.Begin (); i.WriteU16 (0); ENSURE_WRITTEN_BYTES (buffer, 8, 0, 0, 0x63, 0x69, 0xde, 0xad, 0xbe, 0xaf); buffer.AddAtEnd (2); i = buffer.Begin (); i.Next (8); i.WriteU16 (0); ENSURE_WRITTEN_BYTES (buffer, 10, 0, 0, 0x63, 0x69, 0xde, 0xad, 0xbe, 0xaf, 0, 0); buffer.RemoveAtStart (3); i = buffer.Begin (); ENSURE_WRITTEN_BYTES (buffer, 7, 0x69, 0xde, 0xad, 0xbe, 0xaf, 0, 0); buffer.RemoveAtEnd (4); i = buffer.Begin (); ENSURE_WRITTEN_BYTES (buffer, 3, 0x69, 0xde, 0xad); buffer.AddAtStart (1); i = buffer.Begin (); i.WriteU8 (0xff); ENSURE_WRITTEN_BYTES (buffer, 4, 0xff, 0x69, 0xde, 0xad); buffer.AddAtEnd (1); i = buffer.Begin (); i.Next (4); i.WriteU8 (0xff); i.Prev (2); uint16_t saved = i.ReadU16 (); i.Prev (2); i.WriteHtonU16 (0xff00); i.Prev (2); if (i.ReadNtohU16 () != 0xff00) { result = false; } i.Prev (2); i.WriteU16 (saved); ENSURE_WRITTEN_BYTES (buffer, 5, 0xff, 0x69, 0xde, 0xad, 0xff); Buffer o = buffer; ENSURE_WRITTEN_BYTES (o, 5, 0xff, 0x69, 0xde, 0xad, 0xff); o.AddAtStart (1); i = o.Begin (); i.WriteU8 (0xfe); ENSURE_WRITTEN_BYTES (o, 6, 0xfe, 0xff, 0x69, 0xde, 0xad, 0xff); buffer.AddAtStart (2); i = buffer.Begin (); i.WriteU8 (0xfd); i.WriteU8 (0xfd); ENSURE_WRITTEN_BYTES (o, 6, 0xfe, 0xff, 0x69, 0xde, 0xad, 0xff); ENSURE_WRITTEN_BYTES (buffer, 7, 0xfd, 0xfd, 0xff, 0x69, 0xde, 0xad, 0xff); // test self-assignment { Buffer a = o; a = a; } // test Remove start. buffer = Buffer (5); ENSURE_WRITTEN_BYTES (buffer, 5, 0, 0, 0, 0, 0); buffer.RemoveAtStart (1); ENSURE_WRITTEN_BYTES (buffer, 4, 0, 0, 0, 0); buffer.AddAtStart (1); buffer.Begin ().WriteU8 (0xff); ENSURE_WRITTEN_BYTES (buffer, 5, 0xff, 0, 0, 0, 0); buffer.RemoveAtStart(3); ENSURE_WRITTEN_BYTES (buffer, 2, 0, 0); buffer.AddAtStart (4); buffer.Begin ().WriteHtonU32 (0xdeadbeaf); ENSURE_WRITTEN_BYTES (buffer, 6, 0xde, 0xad, 0xbe, 0xaf, 0, 0); buffer.RemoveAtStart (2); ENSURE_WRITTEN_BYTES (buffer, 4, 0xbe, 0xaf, 0, 0); buffer.AddAtEnd (4); i = buffer.Begin (); i.Next (4); i.WriteHtonU32 (0xdeadbeaf); ENSURE_WRITTEN_BYTES (buffer, 8, 0xbe, 0xaf, 0, 0, 0xde, 0xad, 0xbe, 0xaf); buffer.RemoveAtStart (5); ENSURE_WRITTEN_BYTES (buffer, 3, 0xad, 0xbe, 0xaf); // test Remove end buffer = Buffer (5); ENSURE_WRITTEN_BYTES (buffer, 5, 0, 0, 0, 0, 0); buffer.RemoveAtEnd (1); ENSURE_WRITTEN_BYTES (buffer, 4, 0, 0, 0, 0); buffer.AddAtEnd (2); i = buffer.Begin (); i.Next (4); i.WriteU8 (0xab); i.WriteU8 (0xac); ENSURE_WRITTEN_BYTES (buffer, 6, 0, 0, 0, 0, 0xab, 0xac); buffer.RemoveAtEnd (1); ENSURE_WRITTEN_BYTES (buffer, 5, 0, 0, 0, 0, 0xab); buffer.RemoveAtEnd (3); ENSURE_WRITTEN_BYTES (buffer, 2, 0, 0); buffer.AddAtEnd (6); i = buffer.Begin (); i.Next (2); i.WriteU8 (0xac); i.WriteU8 (0xad); i.WriteU8 (0xae); i.WriteU8 (0xaf); i.WriteU8 (0xba); i.WriteU8 (0xbb); ENSURE_WRITTEN_BYTES (buffer, 8, 0, 0, 0xac, 0xad, 0xae, 0xaf, 0xba, 0xbb); buffer.AddAtStart (3); i = buffer.Begin (); i.WriteU8 (0x30); i.WriteU8 (0x31); i.WriteU8 (0x32); ENSURE_WRITTEN_BYTES (buffer, 11, 0x30, 0x31, 0x32, 0, 0, 0xac, 0xad, 0xae, 0xaf, 0xba, 0xbb); buffer.RemoveAtEnd (9); ENSURE_WRITTEN_BYTES (buffer, 2, 0x30, 0x31); buffer = Buffer (3); buffer.AddAtEnd (2); i = buffer.Begin (); i.Next (3); i.WriteHtonU16 (0xabcd); buffer.AddAtStart (1); buffer.Begin ().WriteU8 (0x21); ENSURE_WRITTEN_BYTES (buffer, 6, 0x21, 0, 0, 0, 0xab, 0xcd); buffer.RemoveAtEnd (8); if (buffer.GetSize () != 0) { result = false; } buffer = Buffer (6); buffer.AddAtStart (9); buffer.AddAtEnd (3); i = buffer.End (); i.Prev (1); i.WriteU8 (1, 1); buffer = Buffer (6); buffer.AddAtStart (3); buffer.RemoveAtEnd (8); buffer.AddAtEnd (4); i = buffer.End (); i.Prev (4); i.WriteU8 (1, 4); buffer = Buffer (1); buffer.AddAtEnd (100); i = buffer.End (); i.Prev (100); i.WriteU8 (1, 100); #if 0 buffer = Buffer (10); ENSURE_WRITTEN_BYTES (buffer, 10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); buffer.Begin ().WriteU8 (1); ENSURE_WRITTEN_BYTES (buffer, 10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); #endif // Bug #54 { const uint32_t actualSize = 72602; const uint32_t chunkSize = 67624; UniformVariable bytesRng (0, 256); Buffer inputBuffer; Buffer outputBuffer; inputBuffer.AddAtEnd (actualSize); { Buffer::Iterator iter = inputBuffer.Begin (); for (uint32_t i = 0; i < actualSize; i++) iter.WriteU8 (static_cast (bytesRng.GetValue ())); } outputBuffer.AddAtEnd (chunkSize); Buffer::Iterator iter = outputBuffer.End (); iter.Prev (chunkSize); iter.Write (inputBuffer.PeekData (), chunkSize); NS_TEST_ASSERT (memcmp (inputBuffer.PeekData (), outputBuffer.PeekData (), chunkSize) == 0); } return result; } static BufferTest gBufferTest; } // namespace ns3 #endif /* RUN_SELF_TESTS */