merge unfinished packet history code
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Tue, 29 May 2007 16:09:20 +0200
changeset 789 1e19322e9012
parent 696 fb446e9ec33d
child 790 de06cb662828
merge unfinished packet history code
SConstruct
src/common/packet-history.cc
src/common/packet-history.h
src/common/packet.cc
src/common/packet.h
--- a/SConstruct	Fri May 25 19:14:05 2007 +0100
+++ b/SConstruct	Tue May 29 16:09:20 2007 +0200
@@ -188,6 +188,7 @@
     'chunk.cc',
     'header.cc',
     'trailer.cc',
+    'packet-history.cc',
     'packet.cc',
     'tags.cc',
     'pcap-writer.cc',
@@ -209,6 +210,7 @@
     'trailer.h',
     'tags.h',
     'packet.h',
+    'packet-history.h',
     'uv-trace-source.h',
     'sv-trace-source.h',
     'fv-trace-source.h',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/packet-history.cc	Tue May 29 16:09:20 2007 +0200
@@ -0,0 +1,985 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006,2007 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 <mathieu.lacage@sophia.inria.fr>
+ */
+#include <utility>
+#include "ns3/assert.h"
+#include "ns3/fatal-error.h"
+#include "packet-history.h"
+#include "chunk.h"
+#include "buffer.h"
+
+namespace ns3 {
+
+bool PacketHistory::m_enable = false;
+PacketHistory::ChunkFactories PacketHistory::m_factories;
+uint32_t PacketHistory::m_maxSize = 0;
+PacketHistory::DataFreeList PacketHistory::m_freeList;
+
+void 
+PacketHistory::Enable (void)
+{
+  m_enable = true;
+}
+
+
+PacketHistory::PacketHistory ()
+  : m_data (0),
+    m_end (0),
+    m_n (0)
+{
+  Construct (0, 0);
+}
+PacketHistory::PacketHistory (uint32_t uid, uint32_t size)
+  : m_data (0),
+    m_end (0),
+    m_n (0)
+{
+  Construct (uid, size);
+}
+void
+PacketHistory::Construct (uint32_t uid, uint32_t size)
+{
+  if (m_enable) 
+    {
+      m_data = PacketHistory::Create (size);
+      AppendOneCommand (PacketHistory::INIT_UID,
+                        uid);
+    }
+}
+PacketHistory::PacketHistory (PacketHistory const &o)
+  : m_data (o.m_data),
+    m_end (o.m_end),
+    m_n (o.m_n)
+{
+  if (m_data != 0) 
+    {
+      m_data->m_count++;
+    }
+}
+PacketHistory &
+PacketHistory::operator = (PacketHistory const& o)
+{
+  if (m_data == o.m_data) 
+    {
+      // self assignment
+      return *this;
+    }
+  if (m_data != 0) 
+    {
+      m_data->m_count--;
+      if (m_data->m_count == 0) 
+        {
+          PacketHistory::Recycle (m_data);
+        }
+    }
+  m_data = o.m_data;
+  m_end = o.m_end;
+  m_n = o.m_n;
+  if (m_data != 0) 
+    {
+      m_data->m_count++;
+    }
+  return *this;
+}
+PacketHistory::~PacketHistory ()
+{
+  if (m_data != 0) 
+    {
+      m_data->m_count--;
+      if (m_data->m_count == 0) 
+        {
+          PacketHistory::Recycle (m_data);
+        }
+    }
+}
+
+
+struct PacketHistory::CommandData *
+PacketHistory::Create (uint32_t size)
+{
+  if (size > m_maxSize)
+    {
+      m_maxSize = size;
+    }
+  while (!m_freeList.empty ()) 
+    {
+      struct PacketHistory::CommandData *data = m_freeList.back ();
+      m_freeList.pop_back ();
+      if (data->m_size >= size) 
+        {
+          data->m_count = 1;
+          return data;
+        }
+      PacketHistory::Deallocate (data);
+    }
+  return PacketHistory::Allocate (size);
+}
+
+void
+PacketHistory::Recycle (struct CommandData *data)
+{
+  NS_ASSERT (data->m_count == 0);
+  if (m_freeList.size () > 1000 ||
+      data->m_size < m_maxSize) 
+    {
+      PacketHistory::Deallocate (data);
+    } 
+  else 
+    {
+      m_freeList.push_back (data);
+    }
+}
+
+struct PacketHistory::CommandData *
+PacketHistory::Allocate (uint32_t n)
+{
+  uint32_t size = sizeof (struct CommandData);
+  if (n <= 4)
+    {
+      n = 4;
+    }
+  size += (n-4) * (4 + 1);
+  uint8_t *buf = new uint8_t [size];
+  struct CommandData *data = (struct CommandData *)buf;
+  data->m_size = n;
+  data->m_count = 1;
+  return data;
+}
+void 
+PacketHistory::Deallocate (struct CommandData *data)
+{
+  uint8_t *buf = (uint8_t *)data;
+  delete [] buf;
+}
+
+void
+PacketHistory::AppendOneCommand (uint32_t type, uint32_t data0, uint32_t data1)
+{
+  NS_ASSERT (m_data != 0);
+  uint32_t n = GetUleb128Size (type);
+  n += GetUleb128Size (data0);
+  n += GetUleb128Size (data1);
+
+  if (m_data->m_size > m_end + n)
+    {
+      if (m_data->m_count == 1 ||
+          m_data->m_dirtyEnd == m_end)
+        {
+          AppendValue (data1);
+          AppendValue (data0);
+          AppendValue (type);
+          m_n++;
+          return;
+        }
+      else
+        {
+          uint8_t *buffer = &(m_data->m_data[m_end]);
+          uint32_t lastType = ReadValue (&buffer);
+          if (lastType == type)
+            {
+              uint32_t lastData = ReadValue (&buffer);
+              if (lastData == data0)
+                {
+                  lastData = ReadValue (&buffer);
+                  if (lastData == data1)
+                    {
+                      return;
+                    }
+                }
+            }
+        }
+    }
+  Reserve (n);
+  AppendValue (data1);
+  AppendValue (data0);
+  AppendValue (type);
+  m_n++;
+}
+
+void
+PacketHistory::AppendOneCommand (uint32_t type, uint32_t data)
+{
+  NS_ASSERT (m_data != 0);
+  uint32_t n = GetUleb128Size (data);
+  n += GetUleb128Size (type);
+
+  if (m_data->m_size > m_end + n)
+    {
+      if (m_data->m_count == 1 ||
+          m_data->m_dirtyEnd == m_end)
+        {
+          AppendValue (data);
+          AppendValue (type);
+          m_n++;
+          return;
+        }
+      else
+        {
+          uint8_t *buffer = &(m_data->m_data[m_end]);
+          uint32_t lastType = ReadValue (&buffer);
+          if (lastType == type)
+            {
+              uint32_t lastData = ReadValue (&buffer);
+              if (lastData == data)
+                {
+                  return;
+                }
+            }
+        }
+    }
+  Reserve (n);
+  AppendValue (data);
+  AppendValue (type);
+  m_n++;
+}
+void
+PacketHistory::ReserveCopy (uint32_t size)
+{
+  struct CommandData *newData = PacketHistory::Create (m_end + size);
+  memcpy (newData->m_data, m_data->m_data, m_end);
+  newData->m_dirtyEnd = m_end;
+  m_data->m_count--;
+  if (m_data->m_count == 0) 
+    {
+      PacketHistory::Recycle (m_data);
+    }
+  m_data = newData;
+}
+void
+PacketHistory::Reserve (uint32_t size)
+{
+  NS_ASSERT (m_data != 0);
+  if (m_data->m_size > m_end + size &&
+      (m_data->m_count == 1 ||
+       m_data->m_dirtyEnd == m_end))
+    {
+      /* enough room, not dirty. */
+    }
+  else 
+    {
+      /* (enough room and dirty) or (not enough room) */
+      ReserveCopy (size);
+    }
+}
+
+uint32_t 
+PacketHistory::GetUleb128Size (uint32_t value) const
+{
+  uint32_t n = 0;
+  uint32_t tmp = value;
+  do {
+    tmp >>= 7;
+    n++;
+  } while (tmp != 0);
+  return n;
+}
+
+uint32_t 
+PacketHistory::GetReverseUleb128Size (uint8_t *buffer) const
+{
+  uint8_t *cur = buffer;
+  uint8_t byte = *cur;
+  if (byte & 0x80)
+    {
+      NS_FATAL_ERROR ("The last byte should have a zero high bit");
+    }
+  cur--;
+  while ((byte & 0x80) && (buffer - cur) < 5)
+    {
+      cur--;
+      byte = *cur;
+    }
+  uint32_t n = buffer - cur;
+  return n;
+}
+
+void
+PacketHistory::AppendValue (uint32_t value)
+{
+  uint32_t n = 0;
+  uint8_t *buffer = &m_data->m_data[m_end];
+  do {
+    uint8_t byte = value & (~0x80);
+    value >>= 7;
+    if (value != 0)
+      {
+        /* more bytes to come */
+        byte |= 0x80;
+      }
+    n++;
+    *buffer = byte;
+    buffer++;
+  } while (value != 0);
+  m_end += n;
+  if (m_end > m_data->m_dirtyEnd)
+    {
+      m_data->m_dirtyEnd = m_end;
+    }
+}
+
+uint32_t
+PacketHistory::ReadValue (uint8_t **pBuffer) const
+{
+  uint32_t n = GetReverseUleb128Size (*pBuffer);
+  NS_ASSERT (n > 0);
+  uint8_t *buffer = *pBuffer - n + 1;
+  uint32_t result = 0;
+  uint8_t shift, byte;
+  result = 0;
+  shift = 0;
+  do {
+    byte = *buffer;
+    buffer++;
+    result |= (byte & (~0x80))<<shift;
+    shift += 7;
+  } while (byte & 0x80 && 
+           /* a LEB128 unsigned number is at most 5 bytes long. */
+           shift < (7*5)); 
+  if (byte & 0x80)
+    {
+      /* This means that the LEB128 number was not valid.
+       * ie: the last (5th) byte did not have the high-order bit zeroed.
+       */
+      result = -1;
+    }
+  *pBuffer = *pBuffer - n;
+  return result;
+}
+
+PacketHistory 
+PacketHistory::CreateFragment (uint32_t start, uint32_t length) const
+{
+  return *this;
+}
+
+void 
+PacketHistory::AddHeader (uint32_t uid, Chunk const & header, uint32_t size)
+{
+  if (m_enable) 
+    {
+      AppendOneCommand (PacketHistory::ADD_HEADER, uid, size);
+    }  
+}
+void 
+PacketHistory::RemoveHeader (uint32_t uid, Chunk const & header, uint32_t size)
+{
+  if (m_enable) 
+    {
+      AppendOneCommand (PacketHistory::REM_HEADER, uid, size);
+    }
+}
+void 
+PacketHistory::AddTrailer (uint32_t uid, Chunk const & trailer, uint32_t size)
+{
+  if (m_enable) 
+    {
+      AppendOneCommand (PacketHistory::ADD_TRAILER, uid, size);
+    }
+}
+void 
+PacketHistory::RemoveTrailer (uint32_t uid, Chunk const & trailer, uint32_t size)
+{
+  if (m_enable) 
+    {
+      AppendOneCommand (PacketHistory::REM_TRAILER, uid, size);
+    }
+}
+void
+PacketHistory::AddAtEnd (PacketHistory const&o)
+{
+  if (m_enable) 
+    {
+      uint32_t n = GetUleb128Size (PacketHistory::ADD_AT_END);
+      n += o.m_end;
+      Reserve (n);
+      memcpy (&m_data->m_data[m_end], o.m_data->m_data, o.m_end);
+      m_end += o.m_end;
+      if (m_end > m_data->m_dirtyEnd)
+        {
+          m_data->m_dirtyEnd = m_end;
+        }
+      AppendOneCommand (PacketHistory::ADD_AT_END, 0);
+    }
+}
+void
+PacketHistory::AddPaddingAtEnd (uint32_t end)
+{
+  if (m_enable)
+    {
+      AppendOneCommand (PacketHistory::PADDING_AT_END, end);
+    }
+}
+void 
+PacketHistory::RemoveAtStart (uint32_t start)
+{
+  if (m_enable) 
+    {
+      AppendOneCommand (PacketHistory::REM_AT_START, start);
+    }
+}
+void 
+PacketHistory::RemoveAtEnd (uint32_t end)
+{
+  if (m_enable) 
+    {
+      AppendOneCommand (PacketHistory::REM_AT_END, end);
+    }
+}
+
+void 
+PacketHistory::PrintDefault (std::ostream &os, Buffer buffer) const
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+
+  Buffer original = buffer;
+  HeadersToPrint headersToPrint;
+  TrailersToPrint trailersToPrint;
+  uint8_t *dataBuffer = &m_data->m_data[m_end] - 1;
+  int32_t start = 0;
+  int32_t end = buffer.GetSize ();
+  int32_t curStart = start;
+  int32_t curEnd = end;
+  for (uint32_t i = 0; i < m_n; i++)
+    {
+      uint32_t type = ReadValue (&dataBuffer);
+      uint32_t data = ReadValue (&dataBuffer);
+      switch (type)
+        {
+        case PacketHistory::INIT_UID:
+          break;
+        case PacketHistory::INIT_SIZE:
+          std::cout << "init size=" << data << std::endl;
+          break;
+        case PacketHistory::ADD_HEADER: {
+          int32_t size = ReadValue (&dataBuffer);
+          if (curStart == start)
+            {
+              if (start + size < end)
+                {
+                  headersToPrint.push_back (std::make_pair (data, curStart));
+                }
+              curStart += size;
+              start += size;
+            }
+          else if (curStart + size <= start)
+            {
+              // header lies entirely outside of data area.
+              curStart += size;
+            }
+          else if (curStart < start)
+            {
+              // header lies partly inside and outside of data area
+              // potentially, because we fragmented the packet in the middle
+              // of this header.
+              curStart += size;
+            }
+          else
+            {
+              // header lies entirely inside data area but there is some 
+              // data at the start of the data area which does not belong
+              // to this header. Potentially, because we fragmented
+              // the packet in the middle of a previous header.
+              NS_ASSERT (curStart > start);
+              // we print the content of the header anyway because we can.
+              if (start + size < end)
+                {
+                  headersToPrint.push_back (std::make_pair (data, curStart));
+                }
+              curStart += size;
+              start = curStart;
+            }
+        } break;
+        case PacketHistory::REM_HEADER: {
+          int32_t size = ReadValue (&dataBuffer);
+          if (curStart <= start)
+            {
+              // header lies entirely outside of data area.
+              curStart -= size;
+            }
+          else
+            {
+              NS_ASSERT (false);
+            }
+        } break;
+        case PacketHistory::ADD_TRAILER: {
+          int32_t size = ReadValue (&dataBuffer);
+          if (curEnd == end)
+            {
+              if (end - size >= start)
+                {
+                  // trailer lies exactly at the end of the data area
+                  trailersToPrint.push_back (std::make_pair (data, buffer.GetSize () - curEnd));
+                }
+              curEnd -= size;
+              end -= size;
+            }
+          else if (curEnd - size >= end)
+            {
+              // trailer lies entirely outside of data area.
+              curEnd -= size;
+            }
+          else if (curEnd > end)
+            {
+              // header lies partly inside and partly outside of
+              // data area, potentially because of fragmentation.
+              curEnd -= size;
+            }
+          else
+            {
+              // header lies entirely inside data area
+              NS_ASSERT (curEnd < end);
+              if (end - size >= start)
+                {
+                  trailersToPrint.push_back (std::make_pair (data, buffer.GetSize () - curEnd));
+                }
+              curEnd -= size;
+              end = curEnd;
+
+            }
+        } break;
+        case PacketHistory::REM_TRAILER: {
+          int32_t size = ReadValue (&dataBuffer);
+          if (curEnd >= end)
+            {
+              curEnd += size;
+            }
+          else
+            {
+              NS_ASSERT (false);
+            }
+        } break;
+        case PacketHistory::REM_AT_START:
+          curStart -= data;
+          break;
+        case PacketHistory::REM_AT_END:
+          curEnd += data;
+          break;
+        }
+    }
+  for (HeadersToPrint::iterator j = headersToPrint.begin (); 
+       j != headersToPrint.end (); j++)
+    {
+      uint32_t uid = j->first;
+      uint32_t offset = j->second;
+      Buffer tmp = original;
+      tmp.RemoveAtStart (offset);
+      Chunk *chunk = CreateStatic (uid);
+      chunk->Deserialize (tmp.Begin ());
+      chunk->Print (os);
+    }
+  for (TrailersToPrint::reverse_iterator j = trailersToPrint.rbegin (); 
+       j != trailersToPrint.rend (); j++)
+    {
+      uint32_t uid = j->first;
+      uint32_t offset = j->second;
+      Buffer tmp = original;
+      tmp.RemoveAtEnd (offset);
+      Chunk *chunk = CreateStatic (uid);
+      chunk->Deserialize (tmp.End ());
+      chunk->Print (os);
+    }
+}
+
+
+Chunk *
+PacketHistory::CreateStatic (uint32_t uid)
+{
+  for (ChunkFactoriesI i = m_factories.begin (); i != m_factories.end (); i++) 
+    {
+      if (i->first == uid) 
+        {
+          return (*i->second) ();
+        }
+    }
+  NS_ASSERT_MSG (false, "cannot be reached");
+  /* quiet compiler */
+  return 0;
+}
+uint32_t 
+PacketHistory::RegisterChunkFactory (Chunk *(*createStatic) (void))
+{
+  static uint32_t uid = 0;
+  uid++;
+  for (ChunkFactoriesI i = m_factories.begin (); i != m_factories.end (); i++) 
+    {
+      NS_ASSERT (i->first != uid);
+    }
+  m_factories.push_back (std::make_pair (uid, createStatic));
+  return uid;
+}
+
+}; // namespace ns3
+
+#include <stdarg.h>
+#include <iostream>
+#include "ns3/test.h"
+#include "header.h"
+#include "trailer.h"
+#include "packet.h"
+
+namespace ns3 {
+
+static std::list<int> g_prints;
+static bool g_headerError;
+static bool g_trailerError;
+
+static void 
+RecordPrint (int n)
+{
+  g_prints.push_back (n);
+}
+
+static void
+RecordTrailerError (int n)
+{
+  g_trailerError = true;
+}
+
+static void
+RecordHeaderError (int n)
+{
+  g_headerError = true;
+}
+
+static void
+CleanupPrints (void)
+{
+  g_prints.erase (g_prints.begin (), g_prints.end ());
+}
+
+template <int N>
+class HistoryHeader : public Header
+{
+private:
+  virtual void PrintTo (std::ostream &os) const;
+  virtual uint32_t GetSerializedSize (void) const;
+  virtual void SerializeTo (Buffer::Iterator start) const;
+  virtual uint32_t DeserializeFrom (Buffer::Iterator start);
+};
+
+template <int N>
+void 
+HistoryHeader<N>::PrintTo (std::ostream &os) const
+{
+  RecordPrint (N);
+}
+template <int N>
+uint32_t 
+HistoryHeader<N>::GetSerializedSize (void) const
+{
+  return N;
+}
+template <int N>
+void 
+HistoryHeader<N>::SerializeTo (Buffer::Iterator start) const
+{
+  start.WriteU8 (N, N);
+}
+template <int N>
+uint32_t
+HistoryHeader<N>::DeserializeFrom (Buffer::Iterator start)
+{
+  for (int i = 0; i < N; i++)
+    {
+      if (start.ReadU8 () != N)
+        {
+          RecordHeaderError (N);
+        }
+    }
+  return N;
+}
+
+template <int N>
+class HistoryTrailer : public Trailer
+{
+private:
+  virtual void PrintTo (std::ostream &os) const;
+  virtual uint32_t GetSerializedSize (void) const;
+  virtual void SerializeTo (Buffer::Iterator start) const;
+  virtual uint32_t DeserializeFrom (Buffer::Iterator start);
+};
+
+template <int N>
+void 
+HistoryTrailer<N>::PrintTo (std::ostream &os) const
+{
+  RecordPrint (N);
+}
+template <int N>
+uint32_t 
+HistoryTrailer<N>::GetSerializedSize (void) const
+{
+  return N;
+}
+template <int N>
+void 
+HistoryTrailer<N>::SerializeTo (Buffer::Iterator start) const
+{
+  start.WriteU8 (N, N);
+}
+template <int N>
+uint32_t
+HistoryTrailer<N>::DeserializeFrom (Buffer::Iterator start)
+{
+  start.Prev (N);
+  for (int i = 0; i < N; i++)
+    {
+      if (start.ReadU8 () != N)
+        {
+          RecordTrailerError (N);
+        }
+    }
+  return N;
+}
+
+
+
+class PacketHistoryTest : public Test {
+public:
+  PacketHistoryTest ();
+  virtual ~PacketHistoryTest ();
+  bool CheckHistory (Packet p, char *file, int line, uint32_t n, ...);
+  virtual bool RunTests (void);
+};
+
+PacketHistoryTest::PacketHistoryTest ()
+  : Test ("PacketHistory")
+{}
+
+PacketHistoryTest::~PacketHistoryTest ()
+{}
+
+bool 
+PacketHistoryTest::CheckHistory (Packet p, char *file, int line, uint32_t n, ...)
+{
+  g_headerError = false;
+  g_trailerError = false;
+  va_list ap;
+  p.Print (std::cerr);
+  va_start (ap, n);
+  if (g_headerError)
+    {
+      std::cout << "PacketHistory header error. file=" << file 
+                << ", line=" << line << std::endl;
+      return false;
+    }
+  if (g_trailerError)
+    {
+      std::cout << "PacketHistory trailer error. file=" << file 
+                << ", line=" << line << std::endl;
+      return false;
+    }
+  if (n != g_prints.size ())
+    {
+      goto error;
+    }
+  for (std::list<int>::iterator i = g_prints.begin (); 
+       i != g_prints.end (); i++)
+    {
+      int v = va_arg (ap, int);
+      if (v != *i)
+        {
+          va_end (ap);
+          goto error;
+        }
+    }
+  va_end (ap);
+  return true;
+ error:
+  std::cout << "PacketHistory error. file="<< file 
+            << ", line=" << line << ", got:\"";
+  for (std::list<int>::iterator i = g_prints.begin (); 
+       i != g_prints.end (); i++)
+    {
+      std::cout << *i << ", ";
+    }
+  std::cout << "\", expected: \"";
+  va_start (ap, n);
+  for (uint32_t j = 0; j < n; j++)
+    {
+      int v = va_arg (ap, int);
+      std::cout << v << ", ";
+    }
+  va_end (ap);
+  std::cout << "\"" << std::endl;
+  return false;
+}
+
+#define ADD_HEADER(p, n)                        \
+  {                                             \
+    HistoryHeader<n> header;                    \
+    p.AddHeader (header);                       \
+  }
+#define ADD_TRAILER(p, n)                       \
+  {                                             \
+    HistoryTrailer<n> trailer;                  \
+    p.AddTrailer (trailer);                     \
+  }
+#define REM_HEADER(p, n)                        \
+  {                                             \
+    HistoryHeader<n> header;                    \
+    p.RemoveHeader (header);                    \
+  }
+#define REM_TRAILER(p, n)                       \
+  {                                             \
+    HistoryTrailer<n> trailer;                  \
+    p.RemoveTrailer (trailer);                  \
+  }
+#define CHECK_HISTORY(p, ...)                   \
+  {                                             \
+    if (!CheckHistory (p, __FILE__,             \
+                      __LINE__, __VA_ARGS__))   \
+      {                                         \
+        ok = false;                             \
+      }                                         \
+    CleanupPrints ();                           \
+  }
+
+bool
+PacketHistoryTest::RunTests (void)
+{
+  bool ok = true;
+
+  PacketHistory::Enable ();
+
+  Packet p = Packet (0);
+  Packet p1 = Packet (0);
+
+  p = Packet (10);
+  ADD_TRAILER (p, 100);
+  CHECK_HISTORY (p, 1, 100);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  CHECK_HISTORY (p, 3, 
+                 3, 2, 1);
+  ADD_HEADER (p, 5);
+  CHECK_HISTORY (p, 4, 
+                 5, 3, 2, 1);
+  ADD_HEADER (p, 6);
+  CHECK_HISTORY (p, 5, 
+                 6, 5, 3, 2, 1);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  REM_HEADER (p, 3);
+  CHECK_HISTORY (p, 2, 
+                 2, 1);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  REM_HEADER (p, 3);
+  REM_HEADER (p, 2);
+  CHECK_HISTORY (p, 1, 
+                 1);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  REM_HEADER (p, 3);
+  REM_HEADER (p, 2);
+  REM_HEADER (p, 1);
+  CHECK_HISTORY (p, 0);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  p1 = p;
+  REM_HEADER (p1, 3);
+  REM_HEADER (p1, 2);
+  REM_HEADER (p1, 1);
+  CHECK_HISTORY (p1, 0);
+  CHECK_HISTORY (p, 3, 
+                 3, 2, 1);
+  ADD_HEADER (p1, 1);
+  ADD_HEADER (p1, 2);
+  CHECK_HISTORY (p1, 2, 
+                 2, 1);
+  CHECK_HISTORY (p, 3, 
+                 3, 2, 1);
+  ADD_HEADER (p, 3);
+  CHECK_HISTORY (p, 4, 
+                 3, 3, 2, 1);
+  ADD_TRAILER (p, 4);
+  CHECK_HISTORY (p, 5, 
+                 3, 3, 2, 1, 4);
+  ADD_TRAILER (p, 5);
+  CHECK_HISTORY (p, 6, 
+                 3, 3, 2, 1, 4, 5);
+  REM_HEADER (p, 3);
+  CHECK_HISTORY (p, 5, 
+                 3, 2, 1, 4, 5);
+  REM_TRAILER (p, 5);
+  CHECK_HISTORY (p, 4, 
+                 3, 2, 1, 4);
+  p1 = p;
+  REM_TRAILER (p, 4);
+  CHECK_HISTORY (p, 3, 
+                 3, 2, 1);
+  CHECK_HISTORY (p1, 4, 
+                 3, 2, 1, 4);
+  p1.RemoveAtStart (3);
+  CHECK_HISTORY (p1, 3, 
+                 2, 1, 4);
+  p1.RemoveAtStart (2);
+  CHECK_HISTORY (p1, 2, 
+                 1, 4);
+  p1.RemoveAtEnd (4);
+  CHECK_HISTORY (p1, 1, 
+                 1);
+  p1.RemoveAtStart (1);
+  CHECK_HISTORY (p1, 0);
+
+  p = Packet (10);
+  ADD_HEADER (p, 8);
+  ADD_TRAILER (p, 8);
+  ADD_TRAILER (p, 8);
+  p.RemoveAtStart (8+10+8);
+  CHECK_HISTORY (p, 1, 8);
+
+
+  p = Packet (10);
+  ADD_HEADER (p, 10);
+  ADD_HEADER (p, 8);
+  ADD_TRAILER (p, 6);
+  ADD_TRAILER (p, 7);
+  ADD_TRAILER (p, 9);
+  p.RemoveAtStart (5);
+  p.RemoveAtEnd (12);
+  CHECK_HISTORY (p, 2, 10, 6);
+
+
+  return ok;
+}
+
+static PacketHistoryTest g_packetHistoryTest;
+
+}//namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/packet-history.h	Tue May 29 16:09:20 2007 +0200
@@ -0,0 +1,188 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006,2007 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 <mathieu.lacage@sophia.inria.fr>
+ */
+#ifndef PACKET_HISTORY_H
+#define PACKET_HISTORY_H
+
+#include <stdint.h>
+#include <vector>
+#include "ns3/callback.h"
+
+namespace ns3 {
+
+class Chunk;
+class Buffer;
+
+class PacketPrinter 
+{
+public:
+  void PrintForward (void);
+  void PrintBackward (void);
+  template <typename T>
+  void Add (Callback<void,std::ostream &, T> cb);
+  
+private:
+  std::vector <std::pair<uint32_t,CallbackBase> > m_printers;
+  bool m_forward;
+};
+
+class PacketHistory {
+public:
+  static void Enable (void);
+
+  PacketHistory (uint32_t uid, uint32_t size);
+  PacketHistory (PacketHistory const &o);
+  PacketHistory &operator = (PacketHistory const& o);
+  ~PacketHistory ();
+
+  template <typename T>
+  void AddHeader (T const &header, uint32_t size);
+  template <typename T>
+  void RemoveHeader (T const &header, uint32_t size);
+
+  template <typename T>
+  void AddTrailer (T const &trailer, uint32_t size);
+  template <typename T>
+  void RemoveTrailer (T const &trailer, uint32_t size);
+
+  PacketHistory CreateFragment (uint32_t start, uint32_t length) const;
+  void AddAtEnd (PacketHistory const&o);
+  void AddPaddingAtEnd (uint32_t end);
+  void RemoveAtStart (uint32_t start);
+  void RemoveAtEnd (uint32_t end);
+
+  void PrintDefault (std::ostream &os, Buffer buffer) const;
+  void Print (std::ostream &os, Buffer buffer, PacketPrinter printer) const;
+
+private:
+  enum CommandType {
+    INIT_UID     = 0,
+    ADD_HEADER   = 1,
+    REM_HEADER   = 2,
+    ADD_TRAILER  = 3,
+    REM_TRAILER  = 4,
+    ADD_AT_END   = 5,
+    REM_AT_START = 6,
+    REM_AT_END   = 7,
+    FRAGMENT     = 8,
+    PADDING_AT_END = 9,
+    INIT_SIZE    = 10,
+    LAST
+  };
+  struct CommandData {
+    uint32_t m_count;
+    uint32_t m_size;
+    uint32_t m_dirtyEnd;
+    uint8_t m_data[8];
+  };
+  typedef std::vector<std::pair<uint32_t,uint32_t> > HeadersToPrint;
+  typedef std::vector<std::pair<uint32_t,uint32_t> > TrailersToPrint;
+  template <typename T>
+  class ChunkUid {
+  public:
+    static const uint32_t GetUid (void);
+    static Chunk *CreateStatic (void);
+  };
+  typedef std::vector<std::pair<uint32_t, Chunk *(*) (void)> > ChunkFactories;
+  typedef std::vector<std::pair<uint32_t, Chunk *(*) (void)> >::iterator ChunkFactoriesI;
+  typedef std::vector<struct CommandData *> DataFreeList;
+  
+  PacketHistory ();
+  void Reserve (uint32_t n);
+  void Construct (uint32_t uid, uint32_t size);
+  uint32_t GetUleb128Size (uint32_t value) const;
+  uint32_t GetReverseUleb128Size (uint8_t *buffer) const;
+  void AppendValue (uint32_t value);
+  uint32_t ReadValue (uint8_t **pBuffer) const;
+  void AppendOneCommand (uint32_t type, uint32_t data);
+  void AppendOneCommand (uint32_t type, uint32_t data0, uint32_t data1);
+  void ReserveCopy (uint32_t size);
+  void AddHeader (uint32_t uid, Chunk const & header, uint32_t size);
+  void RemoveHeader (uint32_t uid, Chunk const & header, uint32_t size);
+  void AddTrailer (uint32_t uid, Chunk const & trailer, uint32_t size);
+  void RemoveTrailer (uint32_t uid, Chunk const & trailer, uint32_t size);
+  static struct PacketHistory::CommandData *Create (uint32_t size);
+  static void Recycle (struct CommandData *data);
+  static struct PacketHistory::CommandData *Allocate (uint32_t n);
+  static void Deallocate (struct CommandData *data);
+  static Chunk *CreateStatic (uint32_t uid);
+  static uint32_t RegisterChunkFactory (Chunk *(*createStatic) (void));  
+  
+  static DataFreeList m_freeList;
+  static ChunkFactories m_factories;
+  static bool m_enable;
+  static uint32_t m_maxSize;
+  
+  struct CommandData *m_data;
+  uint32_t m_end;
+  uint32_t m_n;
+};
+
+}; // namespace ns3
+
+namespace ns3 {
+
+template <typename T>
+const uint32_t 
+PacketHistory::ChunkUid<T>::GetUid (void)
+{
+  static uint32_t uid = 
+    PacketHistory::RegisterChunkFactory (&PacketHistory::ChunkUid<T>::CreateStatic);
+  return uid;
+}
+template <typename T>
+Chunk *
+PacketHistory::ChunkUid<T>::CreateStatic (void)
+{
+  static T chunk = T ();
+  return &chunk;
+}
+
+
+template <typename T>
+void 
+PacketHistory::AddHeader (T const &header, uint32_t size)
+{
+  AddHeader (ChunkUid<T>::GetUid (), header, size);
+}
+
+template <typename T>
+void 
+PacketHistory::RemoveHeader (T const &header, uint32_t size)
+{
+  RemoveHeader (ChunkUid<T>::GetUid (), header, size);
+}
+template <typename T>
+void 
+PacketHistory::AddTrailer (T const &trailer, uint32_t size)
+{
+  AddTrailer (ChunkUid<T>::GetUid (), trailer, size);
+}
+template <typename T>
+void 
+PacketHistory::RemoveTrailer (T const &trailer, uint32_t size)
+{
+  RemoveTrailer (ChunkUid<T>::GetUid (), trailer, size);
+}
+
+}; // namespace ns3
+
+
+#endif /* PACKET_HISTORY_H */
--- a/src/common/packet.cc	Fri May 25 19:14:05 2007 +0100
+++ b/src/common/packet.cc	Tue May 29 16:09:20 2007 +0200
@@ -27,6 +27,7 @@
 
 Packet::Packet ()
   : m_buffer (),
+    m_history (m_globalUid, 0),
     m_uid (m_globalUid)
 {
   m_globalUid++;
@@ -34,12 +35,14 @@
 
 Packet::Packet (uint32_t size)
   : m_buffer (size),
+    m_history (m_globalUid, size),
     m_uid (m_globalUid)
 {
   m_globalUid++;
 }
 Packet::Packet (uint8_t const*buffer, uint32_t size)
   : m_buffer (),
+    m_history (m_globalUid, size),
     m_uid (m_globalUid)
 {
   m_globalUid++;
@@ -48,9 +51,10 @@
   i.Write (buffer, size);
 }
 
-Packet::Packet (Buffer buffer, Tags tags, uint32_t uid)
+Packet::Packet (Buffer buffer, Tags tags, PacketHistory history, uint32_t uid)
   : m_buffer (buffer),
     m_tags (tags),
+    m_history (history),
     m_uid (uid)
 {}
 
@@ -58,7 +62,8 @@
 Packet::CreateFragment (uint32_t start, uint32_t length) const
 {
   Buffer buffer = m_buffer.CreateFragment (start, length);
-  return Packet (buffer, m_tags, m_uid);
+  PacketHistory history = m_history.CreateFragment (start, length);
+  return Packet (buffer, m_tags, history, m_uid);
 }
 
 uint32_t 
@@ -79,21 +84,25 @@
    * XXX: we might need to merge the tag list of the
    * other packet into the current packet.
    */
+  m_history.AddAtEnd (packet.m_history);
 }
 void
 Packet::AddPaddingAtEnd (uint32_t size)
 {
   m_buffer.AddAtEnd (size);
+  m_history.AddPaddingAtEnd (size);
 }
 void 
 Packet::RemoveAtEnd (uint32_t size)
 {
   m_buffer.RemoveAtEnd (size);
+  m_history.RemoveAtEnd (size);
 }
 void 
 Packet::RemoveAtStart (uint32_t size)
 {
   m_buffer.RemoveAtStart (size);
+  m_history.RemoveAtStart (size);
 }
 
 void 
@@ -116,6 +125,8 @@
 
 void 
 Packet::Print (std::ostream &os) const
-{}
+{
+  m_history.PrintDefault (os, m_buffer);
+}
 
 }; // namespace ns3
--- a/src/common/packet.h	Fri May 25 19:14:05 2007 +0100
+++ b/src/common/packet.h	Tue May 29 16:09:20 2007 +0200
@@ -26,6 +26,7 @@
 #include "header.h"
 #include "trailer.h"
 #include "tags.h"
+#include "packet-history.h"
 #include "ns3/callback.h"
 #include "ns3/assert.h"
 
@@ -129,7 +130,7 @@
   uint32_t GetSize (void) const;
   /**
    * Add header to this packet. This method invokes the
-   * ns3::Header::GetSerializedSize and ns3::Header::SerializeTo 
+   * ns3::Chunk::GetSerializedSize and ns3::Chunk::SerializeTo 
    * methods to reserve space in the buffer and request the 
    * header to serialize itself in the packet buffer.
    *
@@ -139,8 +140,7 @@
   void AddHeader (T const &header);
   /**
    * Deserialize and remove the header from the internal buffer.
-   * This method invokes ns3::Header::DeserializeFrom
-   * and then removes the deserialized bytes from the buffer.
+   * This method invokes ns3::Chunk::DeserializeFrom.
    *
    * \param header a reference to the header to remove from the internal buffer.
    * \returns the number of bytes removed from the packet.
@@ -149,7 +149,7 @@
   uint32_t RemoveHeader (T &header);
   /**
    * Add trailer to this packet. This method invokes the
-   * ns3::Trailer::GetSerializedSize and ns3::Trailer::serializeTo 
+   * ns3::Chunk::GetSerializedSize and ns3::Trailer::serializeTo 
    * methods to reserve space in the buffer and request the trailer 
    * to serialize itself in the packet buffer.
    *
@@ -159,8 +159,7 @@
   void AddTrailer (T const &trailer);
   /**
    * Remove a deserialized trailer from the internal buffer.
-   * This method invokes the ns3::Trailer::DeserializeFrom method
-   * and then removes the deserialized bytes from the buffer.
+   * This method invokes the ns3::Chunk::DeserializeFrom method.
    *
    * \param trailer a reference to the trailer to remove from the internal buffer.
    * \returns the number of bytes removed from the end of the packet.
@@ -256,9 +255,10 @@
 
   void Print (std::ostream &os) const;
 private:
-  Packet (Buffer buffer, Tags tags, uint32_t uid);
+  Packet (Buffer buffer, Tags tags, PacketHistory history, uint32_t uid);
   Buffer m_buffer;
   Tags m_tags;
+  PacketHistory m_history;
   uint32_t m_uid;
   static uint32_t m_globalUid;
 };
@@ -282,6 +282,7 @@
   uint32_t size = header.GetSize ();
   m_buffer.AddAtStart (size);
   header.Serialize (m_buffer.Begin ());
+  m_history.AddHeader (header, size);
 }
 template <typename T>
 uint32_t
@@ -291,6 +292,7 @@
                  "Must pass Header subclass to Packet::RemoveHeader");
   uint32_t deserialized = header.Deserialize (m_buffer.Begin ());
   m_buffer.RemoveAtStart (deserialized);
+  m_history.RemoveHeader (header, deserialized);
   return deserialized;
 }
 template <typename T>
@@ -301,8 +303,10 @@
                  "Must pass Trailer subclass to Packet::AddTrailer");
   uint32_t size = trailer.GetSize ();
   m_buffer.AddAtEnd (size);
-  Buffer::Iterator end = m_buffer.End ();
-  trailer.Serialize (end);
+  Buffer::Iterator start = m_buffer.End ();
+  start.Prev (size);
+  trailer.Serialize (start);
+  m_history.AddTrailer (trailer, size);
 }
 template <typename T>
 uint32_t
@@ -312,6 +316,7 @@
                  "Must pass Trailer subclass to Packet::RemoveTrailer");
   uint32_t deserialized = trailer.Deserialize (m_buffer.End ());
   m_buffer.RemoveAtEnd (deserialized);
+  m_history.RemoveTrailer (trailer, deserialized);
   return deserialized;
 }