/* -*- Mode:NS3; -*- */ /* * Copyright (c) 2005,2006 INRIA * All rights reserved. * * 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 #include //#define TRACE(x) std::cout << x << std::endl; #define TRACE(x) namespace ns3 { Buffer::BufferDataList Buffer::m_freeList; uint32_t Buffer::m_maxTotalAddStart = 0; uint32_t Buffer::m_maxTotalAddEnd = 0; struct Buffer::BufferData * Buffer::Allocate (uint32_t reqSize, uint32_t reqStart) { if (reqSize == 0) { reqSize = 1; } assert (reqSize >= 1); uint32_t size = reqSize - 1 + sizeof (struct Buffer::BufferData); uint8_t *b = new uint8_t [size]; struct BufferData *data = reinterpret_cast(b); data->m_size = reqSize; data->m_initialStart = reqStart; data->m_dirtyStart = reqStart; data->m_dirtySize = 0; data->m_count = 1; return data; } void Buffer::Deallocate (struct Buffer::BufferData *data) { uint8_t *buf = reinterpret_cast (data); delete [] buf; } #ifdef USE_FREE_LIST void Buffer::Recycle (struct Buffer::BufferData *data) { assert (data->m_count == 0); /* get rid of it if it is too small for later reuse. */ if (data->m_size < (Buffer::m_maxTotalAddStart + Buffer::m_maxTotalAddEnd)) { Buffer::Deallocate (data); return; } /* feed into free list */ if (Buffer::m_freeList.size () > 1000) { Buffer::Deallocate (data); } else { Buffer::m_freeList.push_back (data); } } Buffer::BufferData * Buffer::Create (void) { /* try to find a buffer correctly sized. */ while (!Buffer::m_freeList.empty ()) { struct Buffer::BufferData *data = Buffer::m_freeList.back (); Buffer::m_freeList.pop_back (); if (data->m_size >= (m_maxTotalAddStart + m_maxTotalAddEnd)) { data->m_initialStart = m_maxTotalAddStart; data->m_dirtyStart = m_maxTotalAddStart; data->m_dirtySize = 0; data->m_count = 1; return data; } Buffer::Deallocate (data); } struct Buffer::BufferData *data = Buffer::Allocate (m_maxTotalAddStart+m_maxTotalAddEnd, m_maxTotalAddStart); assert (data->m_count == 1); return data; } #else void Buffer::Recycle (struct Buffer::BufferData *data) { Buffer::Deallocate (data); } Buffer::BufferData * Buffer::Create (void) { return Buffer::Allocate (m_maxTotalAddStart+m_maxTotalAddEnd, m_maxTotalAddStart); } #endif }; // namespace ns3 #include namespace ns3 { void Buffer::AddAtStart (uint32_t start) { assert (m_start <= m_data->m_initialStart); bool isDirty = m_data->m_count > 1 && m_start > m_data->m_dirtyStart; if (m_start >= start && !isDirty) { /* enough space in the buffer and not dirty. */ m_start -= start; m_size += start; } else if (m_size + start <= m_data->m_size && !isDirty) { /* enough space but need to move data around to fit new data */ memmove (m_data->m_data + start, GetStart (), m_size); assert (start > m_start); m_data->m_initialStart += start; m_start = 0; m_size += start; } else if (m_start < start) { /* not enough space in buffer */ uint32_t newSize = m_size + start; struct Buffer::BufferData *newData = Buffer::Allocate (newSize, 0); memcpy (newData->m_data + start, GetStart (), m_size); newData->m_initialStart = m_data->m_initialStart + start; m_data->m_count--; if (m_data->m_count == 0) { Buffer::Deallocate (m_data); } m_data = newData; m_start = 0; m_size = newSize; } else { /* enough space in the buffer but it is dirty ! */ assert (isDirty); struct Buffer::BufferData *newData = Buffer::Create (); memcpy (newData->m_data + m_start, GetStart (), m_size); newData->m_initialStart = m_data->m_initialStart; m_data->m_count--; if (m_data->m_count == 0) { Recycle (m_data); } m_data = newData; m_start -= start; m_size += start; } // update dirty area m_data->m_dirtyStart = m_start; m_data->m_dirtySize = m_size; // update m_maxTotalAddStart uint32_t addedAtStart; if (m_data->m_initialStart > m_start) { addedAtStart = m_data->m_initialStart - m_start; } else { addedAtStart = 0; } if (addedAtStart > m_maxTotalAddStart) { m_maxTotalAddStart = addedAtStart; } TRACE ("start add="< 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)) { \ ok = false; \ } \ } bool BufferTest::RunTests (void) { bool ok = 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) { ok = 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) { ok = false; } return ok; } static BufferTest gBufferTest; }; // namespace ns3 #endif /* RUN_SELF_TESTS */