merge ns-3-dev
authorTom Henderson <tomh@tomh.org>
Fri, 06 Jul 2007 14:39:59 -0700
changeset 1031 1224e13d80a4
parent 1030 415dbf897bba (current diff)
parent 917 aed7f1b9e783 (diff)
child 1032 d32bc00b565f
merge ns-3-dev
SConstruct
--- a/.hgtags	Fri Jul 06 14:19:40 2007 -0700
+++ b/.hgtags	Fri Jul 06 14:39:59 2007 -0700
@@ -1,3 +1,4 @@
 56928998e05c9c11f5f3aefe79be8d2843e0db88 release ns-3.0.1
 7ac5a4b0969b255c4824c926c2b37ef450136ce9 release ns-3.0.2
+0dc81e76166c56aaae64da48b673b62155943aad packet-history-working
 38099dd26e9467b8f49f8632f22789858149a6e7 release ns-3.0.3
--- a/SConstruct	Fri Jul 06 14:19:40 2007 -0700
+++ b/SConstruct	Fri Jul 06 14:39:59 2007 -0700
@@ -190,6 +190,8 @@
     'chunk.cc',
     'header.cc',
     'trailer.cc',
+    'packet-printer.cc',
+    'packet-metadata.cc',
     'packet.cc',
     'tags.cc',
     'pcap-writer.cc',
@@ -211,6 +213,8 @@
     'trailer.h',
     'tags.h',
     'packet.h',
+    'packet-printer.h',
+    'packet-metadata.h',
     'uv-trace-source.h',
     'sv-trace-source.h',
     'fv-trace-source.h',
@@ -306,8 +310,6 @@
     'udp-impl.cc',
 ])
 inode.add_headers ([
-    'ipv4-header.h',
-    'udp-header.h',
     'ipv4-checksum.h',
     'arp-header.h',
     'arp-cache.h',
@@ -337,6 +339,8 @@
     'internet-node.h',
     'ascii-trace.h',
     'pcap-trace.h',
+    'ipv4-header.h',
+    'udp-header.h',
 ])
 
 
@@ -370,9 +374,9 @@
 bench_object.add_source('bench-object.cc')
 
 bench_packets = build.Ns3Module('bench-packets', 'utils')
-#ns3.add(bench_packets)
+ns3.add(bench_packets)
 bench_packets.set_executable()
-bench_packets.add_dep('core')
+bench_packets.add_deps (['core', 'common'])
 bench_packets.add_source('bench-packets.cc')
 
 bench_simu = build.Ns3Module('bench-simulator', 'utils')
@@ -396,6 +400,13 @@
 sample_debug.add_source('main-debug.cc')
 sample_debug.add_source('main-debug-other.cc')
 
+sample_packet_printer = build.Ns3Module('sample-packet-printer', 'samples')
+sample_packet_printer.set_executable()
+ns3.add(sample_packet_printer)
+sample_packet_printer.add_deps (['common', 'internet-node'])
+sample_packet_printer.add_source('main-packet-printer.cc')
+
+
 sample_callback = build.Ns3Module('sample-callback', 'samples')
 sample_callback.set_executable()
 ns3.add(sample_callback)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/samples/main-packet-printer.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -0,0 +1,197 @@
+/* -*- 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 "ns3/packet.h"
+#include "ns3/header.h"
+#include "ns3/packet-printer.h"
+#include "ns3/ipv4-header.h"
+#include "ns3/udp-header.h"
+
+using namespace ns3;
+
+// This sample file shows how to use the Packet metadata facility
+//
+// Packets are stored as ``packed'' data structures, to facilitate
+// fragmentation and network emulation.  However, when debugging a program,
+// or for certain tracing applications, it may be convenient to dump out
+// the contents of a packet header in a human-friendly form.
+//
+// To do this, a few things are needed:
+// i) enable the metadata facility (disabled by default, because it causes
+//    a small performance hit 
+// ii) decide on whether you want to use a default or customized (you
+//     provide your own) routine to dump a particular header
+//
+// This sample steps through two routines; one to use the default
+// printing of IPv4 and UDP headers, and one to show a non-default case.
+// There is a lot of emphasis in this sample of how this facility
+// interacts with packet fragmentation.
+
+void DefaultPrint (void)
+{
+  // We create a packet with 1000 bytes of zero payload
+  // and add 3 headers to this packet.
+  Packet p (1000);
+  Ipv4Header ipv4;
+  UdpHeader udp;
+  ipv4.SetSource (Ipv4Address ("192.168.0.1"));
+  ipv4.SetDestination (Ipv4Address ("192.168.0.2"));
+  udp.SetSource (1025);
+  udp.SetDestination (80);
+  udp.SetPayloadSize (1000);
+  p.AddHeader (udp);
+  p.AddHeader (ipv4);
+
+  std::cout << "full packet size=" << p.GetSize () << std::endl;
+  // Here, invoke the default Print routine, directed to std out
+  p.Print (std::cout);
+  std::cout << std::endl;
+
+
+  // Now, we fragment our packet in 3 consecutive pieces.
+  Packet p1 = p.CreateFragment (0, 2);
+  Packet p2 = p.CreateFragment (2, 1000);
+  Packet p3 = p.CreateFragment (1002, 26);
+
+  std::cout << "fragment1" << std::endl;
+  p1.Print (std::cout);
+  std::cout << std::endl;
+  std::cout << "fragment2" << std::endl;
+  p2.Print (std::cout);
+  std::cout << std::endl;
+  std::cout << "fragment3" << std::endl;
+  p3.Print (std::cout);
+  std::cout << std::endl;
+
+  // And, finally, we re-aggregate the 3 consecutive pieces.
+  Packet aggregate = p1;
+  aggregate.AddAtEnd (p2);
+  aggregate.AddAtEnd (p3);
+  std::cout << "aggregated" << std::endl;
+  aggregate.Print (std::cout);
+  std::cout << std::endl;
+}
+
+// The below functions are used in place of default versions, in the
+// non-default case below.  For instance, DoPrintIpv4Header will print
+// out less IPv4 header information than the default print function
+void 
+DoPrintDefault (std::ostream &os,uint32_t packetUid, uint32_t size, 
+                std::string &name, struct PacketPrinter::FragmentInformation info)
+{
+  os << name <<" (size " << size << " trim_start " << info.start << " trim_end " << info.end << ")";
+}
+void
+DoPrintPayload (std::ostream & os,uint32_t packetUid,uint32_t size,
+                struct PacketPrinter::FragmentInformation info)
+{
+  os << "PAYLOAD (size " << size << " trim_start " << info.start << " trim_end " << info.end << ")";
+}
+void 
+DoPrintIpv4Header (std::ostream &os, uint32_t packetUid, uint32_t size, const Ipv4Header *ipv4)
+{
+  os << "IPV4 " << ipv4->GetSource () << " > " << ipv4->GetDestination ();
+}
+void 
+DoPrintIpv4HeaderFragment (std::ostream &os, uint32_t packetUid, uint32_t size,
+                          std::string &name, struct PacketPrinter::FragmentInformation info)
+{
+  os << "IPV4 fragment";
+}
+
+// This function walks through a non-default case.  A few features of
+// the API (defined in common/packet-printer.h) are shown.
+//
+void NonDefaultPrint (void)
+{
+  // create an adhoc packet printer.
+  PacketPrinter printer;
+  // print from first header to last trailer
+  printer.PrintForward ();
+  // set a string separator automatically inserted
+  // between each call to a printing function.
+  printer.SetSeparator (" - ");
+  // set the default print function: invoked if no 
+  // specialized function has been provided for a header
+  // or trailer
+  printer.AddDefaultPrinter (MakeCallback (&DoPrintDefault));
+  // set the payload print function
+  printer.AddPayloadPrinter (MakeCallback (&DoPrintPayload));
+  // set the print function for the header type Ipv4Header.
+  printer.AddHeaderPrinter (MakeCallback (&DoPrintIpv4Header),
+                            MakeCallback (&DoPrintIpv4HeaderFragment));
+
+
+  // We create a packet with 1000 bytes of zero payload
+  Packet p (1000);
+  Ipv4Header ipv4;
+  UdpHeader udp;
+  ipv4.SetSource (Ipv4Address ("192.168.0.1"));
+  ipv4.SetDestination (Ipv4Address ("192.168.0.2"));
+  udp.SetSource (1025);
+  udp.SetDestination (80);
+  udp.SetPayloadSize (1000);
+  p.AddHeader (udp);
+  p.AddHeader (ipv4);
+
+  std::cout << "full packet size=" << p.GetSize () << std::endl;
+  p.Print (std::cout, printer);
+  std::cout << std::endl;
+
+
+  // fragment our packet in 3 pieces
+  Packet p1 = p.CreateFragment (0, 2);
+  Packet p2 = p.CreateFragment (2, 1000);
+  Packet p3 = p.CreateFragment (1002, 26);
+  std::cout << "fragment1" << std::endl;
+  p1.Print (std::cout, printer);
+  std::cout << std::endl;
+  std::cout << "fragment2" << std::endl;
+  p2.Print (std::cout, printer);
+  std::cout << std::endl;
+  std::cout << "fragment3" << std::endl;
+  p3.Print (std::cout, printer);
+  std::cout << std::endl;
+
+  // aggregate all 3 fragments of the original packet
+  // to reconstruct a copy of the original packet.
+  Packet aggregate = p1;
+  aggregate.AddAtEnd (p2);
+  aggregate.AddAtEnd (p3);
+  std::cout << "aggregated" << std::endl;
+  aggregate.Print (std::cout, printer);
+  std::cout << std::endl;
+}
+
+
+
+int main (int argc, char *argv[])
+{
+  Packet::EnableMetadata ();
+
+  std::cout << "DefaultPrint()" << std::endl;
+  DefaultPrint ();
+
+  std::cout << std::endl << "NonDefaultPrint()" << std::endl;
+  NonDefaultPrint ();
+
+  return 0;
+}
--- a/samples/main-packet.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/samples/main-packet.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -15,6 +15,7 @@
   void SetData (uint16_t data);
   uint16_t GetData (void) const;
 private:
+  virtual std::string DoGetName (void) const;
   virtual void PrintTo (std::ostream &os) const;
   virtual void SerializeTo (Buffer::Iterator start) const;
   virtual uint32_t DeserializeFrom (Buffer::Iterator start);
@@ -27,6 +28,11 @@
 {}
 MyHeader::~MyHeader ()
 {}
+std::string 
+MyHeader::DoGetName (void) const
+{
+  return "MyHeader";
+}
 void 
 MyHeader::PrintTo (std::ostream &os) const
 {
--- a/src/common/buffer.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/buffer.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -224,7 +224,7 @@
       uint32_t newSize = m_size + end;
       struct Buffer::BufferData *newData = Buffer::Allocate (newSize, 0);
       memcpy (newData->m_data, GetStart (), m_size);
-      newData->m_initialStart = m_data->m_initialStart;
+      newData->m_initialStart = m_data->m_initialStart - m_start;
       m_data->m_count--;
       if (m_data->m_count == 0) 
         {
@@ -663,6 +663,12 @@
   i.Prev (4);
   i.WriteU8 (1, 4);
 
+  buffer = Buffer (1);
+  buffer.AddAtEnd (100);
+  i = buffer.End ();
+  i.Prev (100);
+  i.WriteU8 (1, 100);
+
   return ok;
 }
 
--- a/src/common/buffer.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/buffer.h	Fri Jul 06 14:39:59 2007 -0700
@@ -320,6 +320,8 @@
    */
   inline Buffer::Iterator End (void) const;
 
+  void TransformIntoRealBuffer (void) const;
+
   inline Buffer (Buffer const &o);
   inline Buffer &operator = (Buffer const &o);
   inline Buffer ();
@@ -337,7 +339,6 @@
   typedef std::vector<struct Buffer::BufferData*> BufferDataList;
 
   inline uint8_t *GetStart (void) const;
-  void TransformIntoRealBuffer (void) const;
   static void Recycle (struct Buffer::BufferData *data);
   static struct Buffer::BufferData *Create (void);
   static struct Buffer::BufferData *Allocate (uint32_t size, uint32_t start);
@@ -543,6 +544,8 @@
 {
   NS_ASSERT (start.m_data == end.m_data);
   NS_ASSERT (start.m_current <= end.m_current);
+  NS_ASSERT (start.m_zeroStart == end.m_zeroStart);
+  NS_ASSERT (start.m_zeroEnd == end.m_zeroEnd);
   NS_ASSERT (m_data != start.m_data);
   uint32_t size = end.m_current - start.m_current;
   uint8_t *src = start.m_data + start.GetIndex (size);
--- a/src/common/chunk.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/chunk.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -30,6 +30,11 @@
 Chunk::~Chunk ()
 {}
 
+std::string
+Chunk::GetName (void) const
+{
+  return DoGetName ();
+}
 void 
 Chunk::Print (std::ostream &os) const
 {
--- a/src/common/chunk.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/chunk.h	Fri Jul 06 14:39:59 2007 -0700
@@ -33,11 +33,13 @@
   Chunk ();
   virtual ~Chunk ();
 
+  std::string GetName (void) const;
   void Print (std::ostream &os) const;
   uint32_t GetSize (void) const;
   void Serialize (Buffer::Iterator start) const;
   uint32_t Deserialize (Buffer::Iterator start);
 private:
+  virtual std::string DoGetName (void) const = 0;
   virtual void PrintTo (std::ostream &os) const = 0;
   virtual uint32_t GetSerializedSize (void) const = 0;
   virtual void SerializeTo (Buffer::Iterator i) const = 0;
--- a/src/common/data-rate.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/data-rate.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -23,9 +23,8 @@
 #include "ns3/nstime.h"
 #include "ns3/fatal-error.h"
 
-namespace {
 
-bool
+static bool
 DoParse (const std::string s, uint64_t *v)
 {
   std::string::size_type n = s.find_first_not_of("0123456789.");
@@ -123,7 +122,6 @@
   return true;
 }
 
-}
 
 namespace ns3 {
 
--- a/src/common/header.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/header.h	Fri Jul 06 14:39:59 2007 -0700
@@ -42,8 +42,25 @@
   virtual ~Header ();
 private:
   /**
+   * \returns a user-readable name to identify this type of header.
+   *
+   * The string returned is expected to be a single word with 
+   * all capital letters
+   */
+  virtual std::string DoGetName (void) const = 0;
+  /**
    * \param os the std output stream in which this 
    *       protocol header must print itself.
+   *
+   * Although the header is free to format its output as it
+   * wishes, it is recommended to follow a few rules to integrate
+   * with the packet pretty printer:
+   *   - start with flags, small field values located between a
+   *     pair of parens. Values should be separated by whitespace.
+   *   - follow the parens with the important fields, separated by
+   *     whitespace.
+   * i.e.:
+   * (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5
    */
   virtual void PrintTo (std::ostream &os) const = 0;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/packet-metadata.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -0,0 +1,1714 @@
+/* -*- 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 <list>
+#include "ns3/assert.h"
+#include "ns3/fatal-error.h"
+#include "ns3/debug.h"
+#include "packet-metadata.h"
+#include "chunk.h"
+#include "buffer.h"
+
+NS_DEBUG_COMPONENT_DEFINE ("PacketMetadata");
+
+namespace ns3 {
+
+bool PacketMetadata::m_enable = false;
+uint32_t PacketMetadata::m_maxSize = 0;
+uint16_t PacketMetadata::m_chunkUid = 0;
+PacketMetadata::DataFreeList PacketMetadata::m_freeList;
+bool g_optOne = false;
+
+void 
+PacketMetadata::Enable (void)
+{
+  m_enable = true;
+}
+
+void 
+PacketMetadata::SetOptOne (bool optOne)
+{
+  g_optOne = optOne;
+}
+
+void
+PacketMetadata::ReserveCopy (uint32_t size)
+{
+  struct PacketMetadata::Data *newData = PacketMetadata::Create (m_used + size);
+  memcpy (newData->m_data, m_data->m_data, m_used);
+  newData->m_dirtyEnd = m_used;
+  m_data->m_count--;
+  if (m_data->m_count == 0) 
+    {
+      PacketMetadata::Recycle (m_data);
+    }
+  m_data = newData;
+  if (m_head != 0xffff)
+    {
+      uint8_t *start;
+      NS_ASSERT (m_tail != 0xffff);
+      // clear the next field of the tail
+      start = &m_data->m_data[m_tail];
+      Append16 (0xffff, start);
+      // clear the prev field of the head
+      start = &m_data->m_data[m_head] + 2;
+      Append16 (0xffff, start);
+    }
+}
+void
+PacketMetadata::Reserve (uint32_t size)
+{
+  NS_ASSERT (m_data != 0);
+  if (m_data->m_size >= m_used + size &&
+      (m_head == 0xffff ||
+       m_data->m_count == 1 ||
+       m_data->m_dirtyEnd == m_used))
+    {
+      /* enough room, not dirty. */
+    }
+  else 
+    {
+      /* (enough room and dirty) or (not enough room) */
+      ReserveCopy (size);
+    }
+}
+
+uint32_t 
+PacketMetadata::GetUleb128Size (uint32_t value) const
+{
+  if (value < 0x80)
+    {
+      return 1;
+    }
+  if (value < 0x4000)
+    {
+      return 2;
+    }
+  if (value < 0x200000)
+    {
+      return 3;
+    }
+  if (value < 0x10000000)
+    {
+      return 4;
+    }
+  return 5;
+}
+uint32_t
+PacketMetadata::ReadUleb128 (const uint8_t **pBuffer) const
+{
+  const uint8_t *buffer = *pBuffer;
+  uint32_t result = 0;
+  uint8_t byte;
+  result = 0;
+  byte = buffer[0];
+  result = (byte & (~0x80));
+  if (!(byte & 0x80))
+    {
+      *pBuffer = buffer + 1;
+      return result;
+    }
+  byte = buffer[1];
+  result |= (byte & (~0x80)) << 7;
+  if (!(byte & 0x80))
+    {
+      *pBuffer = buffer + 2;
+      return result;
+    }
+  byte = buffer[2];
+  result |= (byte & (~0x80)) << 14;
+  if (!(byte & 0x80))
+    {
+      *pBuffer = buffer + 3;
+      return result;
+    }
+  byte = buffer[3];
+  result |= (byte & (~0x80)) << 21;
+  if (!(byte & 0x80))
+    {
+      *pBuffer = buffer + 4;
+      return result;
+    }
+  byte = buffer[4];
+  result |= (byte & (~0x80)) << 28;
+  if (!(byte & 0x80))
+    {
+      *pBuffer = buffer + 5;
+      return result;
+    }
+  /* This means that the LEB128 number was not valid.
+   * ie: the last (5th) byte did not have the high-order bit zeroed.
+   */
+  NS_ASSERT (false);
+  return 0;
+}
+
+void
+PacketMetadata::Append16 (uint16_t value, uint8_t *buffer)
+{
+  buffer[0] = value & 0xff;
+  value >>= 8;
+  buffer[1] = value;
+}
+bool
+PacketMetadata::TryToAppendFast (uint32_t value, uint8_t **pBuffer, uint8_t *end)
+{
+  uint8_t *start = *pBuffer;
+  if (value < 0x80 && start < end)
+    {
+      start[0] = value;
+      *pBuffer = start + 1;
+      return true;
+    }
+  if (value < 0x4000 && start + 1 < end)
+    {
+      uint8_t byte = value & (~0x80);
+      start[0] = 0x80 | byte;
+      value >>= 7;
+      start[1] = value;
+      *pBuffer = start + 2;
+      return true;
+    }
+  return false;
+}
+bool
+PacketMetadata::TryToAppend16 (uint16_t value,  uint8_t **pBuffer, uint8_t *end)
+{
+  uint8_t *start = *pBuffer;
+  if (start + 1 < end)
+    {
+      start[0] = value & 0xff;
+      start[1] = value >> 8;
+      *pBuffer = start + 2;
+      return true;
+    }
+  return false;
+}
+bool
+PacketMetadata::TryToAppend32 (uint32_t value,  uint8_t **pBuffer, uint8_t *end)
+{
+  uint8_t *start = *pBuffer;
+  if (start + 3 < end)
+    {
+      start[0] = value & 0xff;
+      start[1] = (value >> 8) & 0xff;
+      start[2] = (value >> 16) & 0xff;
+      start[3] = (value >> 24) & 0xff;
+      *pBuffer = start + 4;
+      return true;
+    }
+  return false;
+}
+bool
+PacketMetadata::TryToAppend (uint32_t value, uint8_t **pBuffer, uint8_t *end)
+{
+  uint8_t *start = *pBuffer;
+  if (value < 0x80 && start < end)
+    {
+      start[0] = value;
+      *pBuffer = start + 1;
+      return true;
+    }
+  if (value < 0x4000 && start + 1 < end)
+    {
+      uint8_t byte = value & (~0x80);
+      start[0] = 0x80 | byte;
+      value >>= 7;
+      start[1] = value;
+      *pBuffer = start + 2;
+      return true;
+    }
+  if (value < 0x200000 && start + 2 < end)
+    {
+      uint8_t byte = value & (~0x80);
+      start[0] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[1] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[2] = value;
+      *pBuffer = start + 3;
+      return true;
+    }
+  if (value < 0x10000000 && start + 3 < end)
+    {
+      uint8_t byte = value & (~0x80);
+      start[0] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[1] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[2] = 0x80 | byte;
+      value >>= 7;
+      start[3] = value;
+      *pBuffer = start + 4;
+      return true;
+    }
+  if (start + 4 < end)
+    {
+      uint8_t byte = value & (~0x80);
+      start[0] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[1] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[2] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      start[3] = 0x80 | byte;
+      value >>= 7;
+      start[4] = value;
+      *pBuffer = start + 5;
+      return true;
+    }
+  return false;
+}
+
+void
+PacketMetadata::AppendValueExtra (uint32_t value, uint8_t *buffer)
+{
+  if (value < 0x200000)
+    {
+      uint8_t byte = value & (~0x80);
+      buffer[0] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      buffer[1] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      buffer[2] = value;
+      return;
+    }
+  if (value < 0x10000000)
+    {
+      uint8_t byte = value & (~0x80);
+      buffer[0] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      buffer[1] = 0x80 | byte;
+      value >>= 7;
+      byte = value & (~0x80);
+      buffer[2] = 0x80 | byte;
+      value >>= 7;
+      buffer[3] = value;
+      return;
+    }
+  {
+    uint8_t byte = value & (~0x80);
+    buffer[0] = 0x80 | byte;
+    value >>= 7;
+    byte = value & (~0x80);
+    buffer[1] = 0x80 | byte;
+    value >>= 7;
+    byte = value & (~0x80);
+    buffer[2] = 0x80 | byte;
+    value >>= 7;
+    byte = value & (~0x80);
+    buffer[3] = 0x80 | byte;
+    value >>= 7;
+    buffer[4] = value;
+  }
+}
+
+void
+PacketMetadata::AppendValue (uint32_t value, uint8_t *buffer)
+{
+  if (value < 0x80)
+    {
+      buffer[0] = value;
+      return;
+    }
+  if (value < 0x4000)
+    {
+      uint8_t byte = value & (~0x80);
+      buffer[0] = 0x80 | byte;
+      value >>= 7;
+      buffer[1] = value;
+      return;
+    }
+  AppendValueExtra (value, buffer);
+}
+
+void
+PacketMetadata::UpdateTail (uint16_t written)
+{
+  if (m_head == 0xffff)
+    {
+      NS_ASSERT (m_tail == 0xffff);
+      m_head = m_used;
+      m_tail = m_used;
+    } 
+  else
+    {
+      NS_ASSERT (m_tail != 0xffff);
+      // overwrite the next field of the previous tail of the list.
+      uint8_t *previousTail = &m_data->m_data[m_tail];
+      Append16 (m_used, previousTail);
+      // update the tail of the list to the new node.
+      m_tail = m_used;
+    }
+  NS_ASSERT (m_tail != 0xffff);
+  NS_ASSERT (m_head != 0xffff);
+  NS_ASSERT (written >= 8);
+  m_used += written;
+  m_data->m_dirtyEnd = m_used;
+}
+
+
+void
+PacketMetadata::UpdateHead (uint16_t written)
+{
+  if (m_head == 0xffff)
+    {
+      NS_ASSERT (m_tail == 0xffff);
+      m_head = m_used;
+      m_tail = m_used;
+    } 
+  else
+    {
+      NS_ASSERT (m_head != 0xffff);
+      // overwrite the prev field of the previous head of the list.
+      uint8_t *previousHead = &m_data->m_data[m_head + 2];
+      Append16 (m_used, previousHead);
+      // update the head of list to the new node.
+      m_head = m_used;
+    }
+  NS_ASSERT (m_tail != 0xffff);
+  NS_ASSERT (m_head != 0xffff);
+  NS_ASSERT (written >= 8);
+  m_used += written;
+  m_data->m_dirtyEnd = m_used;
+}
+
+uint16_t
+PacketMetadata::AddSmall (const struct PacketMetadata::SmallItem *item)
+{
+  NS_ASSERT (m_data != 0);
+  NS_ASSERT (m_used != item->prev && m_used != item->next);
+  if (g_optOne)
+    {
+      uint32_t typeUidSize = GetUleb128Size (item->typeUid);
+      uint32_t sizeSize = GetUleb128Size (item->size);
+      uint32_t n = typeUidSize + sizeSize + 2 + 2 + 2;
+    restart:
+      if (m_used + n <= m_data->m_size &&
+      (m_head == 0xffff ||
+       m_data->m_count == 1 ||
+       m_used == m_data->m_dirtyEnd))
+        {
+          uint8_t *buffer = &m_data->m_data[m_used];
+          Append16 (item->next, buffer);
+          buffer += 2;
+          Append16 (item->prev, buffer);
+          buffer += 2;
+          AppendValue (item->typeUid, buffer);
+          buffer += typeUidSize;
+          AppendValue (item->size, buffer);
+          buffer += sizeSize;
+          Append16 (item->chunkUid, buffer);
+        }
+      else
+        {
+          ReserveCopy (n);
+          goto restart;
+        }
+      return n;
+    }
+ append:
+  uint8_t *start = &m_data->m_data[m_used];
+  uint8_t *end = &m_data->m_data[m_data->m_size];
+  if (end - start >= 8 &&
+      (m_head == 0xffff ||
+       m_data->m_count == 1 ||
+       m_used == m_data->m_dirtyEnd))
+    {
+      uint8_t *buffer = start;
+
+      Append16 (item->next, buffer);
+      buffer += 2;
+      Append16 (item->prev, buffer);
+      buffer += 2;
+      if (TryToAppendFast (item->typeUid, &buffer, end) &&
+          TryToAppendFast (item->size, &buffer, end) &&
+          TryToAppend16 (item->chunkUid, &buffer, end))
+        {
+          uintptr_t written = buffer - start;
+          NS_ASSERT (written <= 0xffff);
+          NS_ASSERT (written >= 8);
+          return written;
+        }
+    }
+  uint32_t n = GetUleb128Size (item->typeUid);
+  n += GetUleb128Size (item->size);
+  n += 2;
+  n += 2 + 2;
+  Reserve (n);
+  goto append;
+}
+
+uint16_t
+PacketMetadata::AddBig (uint32_t next, uint32_t prev, 
+                       const PacketMetadata::SmallItem *item, 
+                       const PacketMetadata::ExtraItem *extraItem)
+{
+  NS_ASSERT (m_data != 0);
+  uint32_t typeUid = ((item->typeUid & 0x1) == 0x1)?item->typeUid:item->typeUid+1;
+  NS_ASSERT (m_used != prev && m_used != next);
+ append:
+  uint8_t *start = &m_data->m_data[m_used];
+  uint8_t *end = &m_data->m_data[m_data->m_size];
+  if (end - start >= 14 &&
+      (m_head == 0xffff ||
+       m_data->m_count == 1 ||
+       m_used == m_data->m_dirtyEnd))
+    {
+      uint8_t *buffer = start;
+
+      Append16 (next, buffer);
+      buffer += 2;
+      Append16 (prev, buffer);
+      buffer += 2;
+      if (TryToAppend (typeUid, &buffer, end) &&
+          TryToAppend (item->size, &buffer, end) &&
+          TryToAppend16 (item->chunkUid, &buffer, end) &&
+          TryToAppend (extraItem->fragmentStart, &buffer, end) &&
+          TryToAppend (extraItem->fragmentEnd, &buffer, end) &&
+          TryToAppend32 (extraItem->packetUid, &buffer, end))
+        {
+          uintptr_t written = buffer - start;
+          NS_ASSERT (written <= 0xffff);
+          NS_ASSERT (written >= 14);
+          return written;
+        }
+    }
+
+  uint32_t n = GetUleb128Size (typeUid);
+  n += GetUleb128Size (item->size);
+  n += 2;
+  n += GetUleb128Size (extraItem->fragmentStart);
+  n += GetUleb128Size (extraItem->fragmentEnd);
+  n += 4;
+  n += 2 + 2;
+  ReserveCopy (n);
+  goto append;
+}
+
+void
+PacketMetadata::ReplaceTail (PacketMetadata::SmallItem *item, 
+                            PacketMetadata::ExtraItem *extraItem,
+                            uint32_t available)
+{
+  NS_ASSERT (m_data != 0);  
+  if (available >= 14 &&
+      m_data->m_count == 1)
+    {
+      uint8_t *buffer = &m_data->m_data[m_tail];
+      uint8_t *end = buffer + available;
+
+      Append16 (item->next, buffer);
+      buffer += 2;
+      Append16 (item->prev, buffer);
+      buffer += 2;
+      if (TryToAppend (item->typeUid, &buffer, end) &&
+          TryToAppend (item->size, &buffer, end) &&
+          TryToAppend16 (item->chunkUid, &buffer, end) &&
+          TryToAppend (extraItem->fragmentStart, &buffer, end) &&
+          TryToAppend (extraItem->fragmentEnd, &buffer, end) &&
+          TryToAppend32 (extraItem->packetUid, &buffer, end))
+        {
+          m_used = buffer - &m_data->m_data[0];
+          m_data->m_dirtyEnd = m_used;
+          return;
+        }
+    }
+  
+  // create a copy of the packet.
+  PacketMetadata h (m_packetUid, 0);
+  uint16_t current = m_head;
+  while (current != 0xffff && current != m_tail)
+    {
+      struct PacketMetadata::SmallItem tmpItem;
+      PacketMetadata::ExtraItem tmpExtraItem;
+      ReadItems (current, &tmpItem, &tmpExtraItem);
+      uint16_t written = h.AddBig (0xffff, h.m_tail, 
+                                   &tmpItem, &tmpExtraItem);
+      h.UpdateTail (written);
+    }
+  // append new tail.
+  uint16_t written = h.AddBig (0xffff, h.m_tail, item, extraItem);
+  h.UpdateTail (written);
+
+  *this = h;
+}
+
+uint32_t
+PacketMetadata::ReadItems (uint16_t current, 
+                          struct PacketMetadata::SmallItem *item,
+                          struct PacketMetadata::ExtraItem *extraItem) const
+{
+  const uint8_t *buffer = &m_data->m_data[current];
+  item->next = buffer[0];
+  item->next |= (buffer[1]) << 8;
+  item->prev = buffer[2];
+  item->prev |= (buffer[3]) << 8;
+  buffer += 4;
+  item->typeUid = ReadUleb128 (&buffer);
+  item->size = ReadUleb128 (&buffer);
+  item->chunkUid = buffer[0];
+  item->chunkUid |= (buffer[1]) << 8;
+  buffer += 2;
+
+  bool isExtra = (item->typeUid & 0x1) == 0x1;
+  if (isExtra)
+    {
+      extraItem->fragmentStart = ReadUleb128 (&buffer);
+      extraItem->fragmentEnd = ReadUleb128 (&buffer);
+      extraItem->packetUid = buffer[0];
+      extraItem->packetUid |= buffer[1] << 8;
+      extraItem->packetUid |= buffer[2] << 16;
+      extraItem->packetUid |= buffer[3] << 24;
+      buffer += 4;
+    }
+  else
+    {
+      extraItem->fragmentStart = 0;
+      extraItem->fragmentEnd = item->size;
+      extraItem->packetUid = m_packetUid;
+    }
+  NS_ASSERT (buffer <= &m_data->m_data[m_data->m_size]);
+  return buffer - &m_data->m_data[current];
+}
+
+struct PacketMetadata::Data *
+PacketMetadata::Create (uint32_t size)
+{
+  NS_DEBUG ("create size="<<size<<", max="<<m_maxSize);
+  if (size > m_maxSize)
+    {
+      m_maxSize = size;
+    }
+  while (!m_freeList.empty ()) 
+    {
+      struct PacketMetadata::Data *data = m_freeList.back ();
+      m_freeList.pop_back ();
+      if (data->m_size >= size) 
+        {
+          NS_DEBUG ("create found size="<<data->m_size);
+          data->m_count = 1;
+          return data;
+        }
+      PacketMetadata::Deallocate (data);
+      NS_DEBUG ("create dealloc size="<<data->m_size);
+    }
+  NS_DEBUG ("create alloc size="<<m_maxSize);
+  return PacketMetadata::Allocate (m_maxSize);
+}
+
+void
+PacketMetadata::Recycle (struct PacketMetadata::Data *data)
+{
+  NS_DEBUG ("recycle size="<<data->m_size<<", list="<<m_freeList.size ());
+  NS_ASSERT (data->m_count == 0);
+  if (m_freeList.size () > 1000 ||
+      data->m_size < m_maxSize) 
+    {
+      PacketMetadata::Deallocate (data);
+    } 
+  else 
+    {
+      m_freeList.push_back (data);
+    }
+}
+
+struct PacketMetadata::Data *
+PacketMetadata::Allocate (uint32_t n)
+{
+  uint32_t size = sizeof (struct Data);
+  if (n <= 10)
+    {
+      n = 10;
+    }
+  size += n - 10;
+  uint8_t *buf = new uint8_t [size];
+  struct PacketMetadata::Data *data = (struct PacketMetadata::Data *)buf;
+  data->m_size = n;
+  data->m_count = 1;
+  data->m_dirtyEnd = 0;
+  return data;
+}
+void 
+PacketMetadata::Deallocate (struct PacketMetadata::Data *data)
+{
+  uint8_t *buf = (uint8_t *)data;
+  delete [] buf;
+}
+
+
+PacketMetadata 
+PacketMetadata::CreateFragment (uint32_t start, uint32_t end) const
+{
+  PacketMetadata fragment = *this;
+  fragment.RemoveAtStart (start);
+  fragment.RemoveAtEnd (end);
+  return fragment;
+}
+
+void 
+PacketMetadata::DoAddHeader (uint32_t uid, uint32_t size)
+{
+  if (!m_enable)
+    {
+      return;
+    }
+  struct PacketMetadata::SmallItem item;
+  item.next = m_head;
+  item.prev = 0xffff;
+  item.typeUid = uid;
+  item.size = size;
+  item.chunkUid = m_chunkUid;
+  m_chunkUid++;
+  uint16_t written = AddSmall (&item);
+  UpdateHead (written);
+}
+void 
+PacketMetadata::DoRemoveHeader (uint32_t uid, uint32_t size)
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+  struct PacketMetadata::SmallItem item;
+  struct PacketMetadata::ExtraItem extraItem;
+  uint32_t read = ReadItems (m_head, &item, &extraItem);
+  if ((item.typeUid & 0xfffffffe) != uid ||
+      item.size != size)
+    {
+      NS_FATAL_ERROR ("Removing unexpected header.");
+    }
+  else if (item.typeUid != uid &&
+           (extraItem.fragmentStart != 0 ||
+            extraItem.fragmentEnd != size))
+    {
+      NS_FATAL_ERROR ("Removing incomplete header.");
+    }
+  if (m_head + read == m_used)
+    {
+      m_used = m_head;
+    }
+  if (item.next == 0xffff)
+    {
+      m_head = 0xffff;
+      m_tail = 0xffff;
+    }
+  else
+    {
+      m_head = item.next;
+    }
+}
+void 
+PacketMetadata::DoAddTrailer (uint32_t uid, uint32_t size)
+{
+  if (!m_enable)
+    {
+      return;
+    }
+  struct PacketMetadata::SmallItem item;
+  item.next = 0xffff;
+  item.prev = m_tail;
+  item.typeUid = uid;
+  item.size = size;
+  item.chunkUid = m_chunkUid;
+  m_chunkUid++;
+  uint16_t written = AddSmall (&item);
+  UpdateTail (written);
+}
+void 
+PacketMetadata::DoRemoveTrailer (uint32_t uid, uint32_t size)
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+  struct PacketMetadata::SmallItem item;
+  struct PacketMetadata::ExtraItem extraItem;
+  uint32_t read = ReadItems (m_tail, &item, &extraItem);
+  if ((item.typeUid & 0xfffffffe) != uid ||
+      item.size != size)
+    {
+      NS_FATAL_ERROR ("Removing unexpected trailer.");
+    }
+  else if (item.typeUid != uid &&
+           (extraItem.fragmentStart != 0 ||
+            extraItem.fragmentEnd != size))
+    {
+      NS_FATAL_ERROR ("Removing incomplete trailer.");
+    }
+  if (m_tail + read == m_used)
+    {
+      m_used = m_tail;
+    }  
+  if (item.prev == 0xffff)
+    {
+      m_head = 0xffff;
+      m_tail = 0xffff;
+    }
+  else
+    {
+      m_tail = item.prev;
+    }
+}
+void
+PacketMetadata::AddAtEnd (PacketMetadata const&o)
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+  if (m_tail == 0xffff)
+    {
+      *this = o;
+      return;
+    }
+  NS_ASSERT (m_head != 0xffff && m_tail != 0xffff);
+
+  uint16_t lastTail;
+  lastTail = m_tail;
+  struct PacketMetadata::SmallItem lastItem;
+  PacketMetadata::ExtraItem lastExtraItem;
+  uint32_t lastTailSize = ReadItems (m_tail, &lastItem, &lastExtraItem);
+  if (m_tail + lastTailSize == m_used &&
+      m_used == m_data->m_dirtyEnd)
+    {
+      lastTailSize = m_data->m_size - m_tail;
+    }
+
+  uint16_t current = o.m_head;
+  while (current != 0xffff)
+    {
+      struct PacketMetadata::SmallItem item;
+      PacketMetadata::ExtraItem extraItem;
+      o.ReadItems (current, &item, &extraItem);
+      if (extraItem.packetUid == lastExtraItem.packetUid &&
+          item.typeUid == lastItem.typeUid &&
+          item.chunkUid == lastItem.chunkUid &&
+          item.size == lastItem.size &&
+          extraItem.fragmentStart == lastExtraItem.fragmentEnd)
+        {
+          // replace previous tail.
+          lastExtraItem.fragmentEnd = extraItem.fragmentEnd;
+          NS_ASSERT (m_tail == lastTail);
+          // XXX This call might be wrong. 
+          ReplaceTail (&lastItem, &lastExtraItem, lastTailSize);
+        }
+      else
+        {
+          // append the extra items.
+          uint16_t written = AddBig (0xffff, m_tail, &item, &extraItem);
+          UpdateTail (written);
+        }
+      if (current == o.m_tail)
+        {
+          break;
+        }
+      current = item.next;
+    }
+}
+void
+PacketMetadata::AddPaddingAtEnd (uint32_t end)
+{
+  if (!m_enable)
+    {
+      return;
+    }
+}
+void 
+PacketMetadata::RemoveAtStart (uint32_t start)
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+  NS_ASSERT (m_data != 0);
+  uint32_t leftToRemove = start;
+  uint16_t current = m_head;
+  while (current != 0xffff && leftToRemove > 0)
+    {
+      struct PacketMetadata::SmallItem item;
+      PacketMetadata::ExtraItem extraItem;
+      ReadItems (current, &item, &extraItem);
+      uint32_t itemRealSize = extraItem.fragmentEnd - extraItem.fragmentStart;
+      if (itemRealSize <= leftToRemove)
+        {
+          // remove from list.
+          m_head = item.next;
+          leftToRemove -= itemRealSize;
+        }
+      else
+        {
+          // fragment the list item.
+          PacketMetadata fragment (m_packetUid, 0);
+          extraItem.fragmentStart += leftToRemove;
+          leftToRemove = 0;
+          uint16_t written = fragment.AddBig (0xffff, fragment.m_tail,
+                                              &item, &extraItem);
+          fragment.UpdateTail (written);
+          current = item.next;
+          while (current != 0xffff)
+            {
+              ReadItems (current, &item, &extraItem);
+              written = fragment.AddBig (0xffff, fragment.m_tail,
+                                         &item, &extraItem);
+              fragment.UpdateTail (written);
+              if (current == m_tail)
+                {
+                  break;
+                }
+              current = item.next;
+            }
+          *this = fragment;
+        }
+      NS_ASSERT (item.size >= extraItem.fragmentEnd - extraItem.fragmentStart &&
+                 extraItem.fragmentStart <= extraItem.fragmentEnd);
+      if (current == m_tail)
+        {
+          break;
+        }
+      current = item.next;
+    }
+  NS_ASSERT (leftToRemove == 0);
+}
+void 
+PacketMetadata::RemoveAtEnd (uint32_t end)
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+  NS_ASSERT (m_data != 0);
+
+  uint32_t leftToRemove = end;
+  uint16_t current = m_tail;
+  while (current != 0xffff && leftToRemove > 0)
+    {
+      struct PacketMetadata::SmallItem item;
+      PacketMetadata::ExtraItem extraItem;
+      ReadItems (current, &item, &extraItem);
+      uint32_t itemRealSize = extraItem.fragmentEnd - extraItem.fragmentStart;
+      if (itemRealSize <= leftToRemove)
+        {
+          // remove from list.
+          m_tail = item.prev;
+          leftToRemove -= itemRealSize;
+        }
+      else
+        {
+          // fragment the list item.
+          PacketMetadata fragment (m_packetUid, 0);
+          NS_ASSERT (extraItem.fragmentEnd > leftToRemove);
+          extraItem.fragmentEnd -= leftToRemove;
+          leftToRemove = 0;
+          uint16_t written = fragment.AddBig (fragment.m_head, 0xffff,
+                                              &item, &extraItem);
+          fragment.UpdateHead (written);
+          current = item.prev;
+          while (current != 0xffff)
+            {
+              ReadItems (current, &item, &extraItem);
+              written = fragment.AddBig (fragment.m_head, 0xffff,
+                                         &item, &extraItem);
+              fragment.UpdateHead (written);
+              if (current == m_head)
+                {
+                  break;
+                }
+              current = item.prev;
+            }
+          *this = fragment;
+        }
+      NS_ASSERT (item.size >= extraItem.fragmentEnd - extraItem.fragmentStart &&
+                 extraItem.fragmentStart <= extraItem.fragmentEnd);
+      if (current == m_head)
+        {
+          break;
+        }
+      current = item.prev;
+    }
+  NS_ASSERT (leftToRemove == 0);
+}
+
+void 
+PacketMetadata::PrintDefault (std::ostream &os, Buffer buffer) const
+{
+  Print (os, buffer, PacketPrinter::GetDefault ());
+}
+
+uint32_t
+PacketMetadata::DoPrint (const struct PacketMetadata::SmallItem *item, 
+                         const struct PacketMetadata::ExtraItem *extraItem,
+                         Buffer data, uint32_t offset, const PacketPrinter &printer,
+                         std::ostream &os) const
+{
+  uint32_t uid = item->typeUid & 0xfffffffe;
+  if (uid == 0)
+    {
+      // payload.
+      printer.PrintPayload (os, extraItem->packetUid, item->size, 
+                            extraItem->fragmentStart, 
+                            item->size - extraItem->fragmentEnd);
+    }
+  else if (extraItem->fragmentStart != 0 ||
+           extraItem->fragmentEnd != item->size)
+    {
+      printer.PrintChunkFragment (uid, os, extraItem->packetUid, item->size, 
+                                  extraItem->fragmentStart, 
+                                  item->size - extraItem->fragmentEnd);
+    }
+  else if (PacketPrinter::IsHeader (uid))
+    {
+      ns3::Buffer::Iterator j = data.Begin ();
+      j.Next (offset);
+      printer.PrintChunk (uid, j, os, extraItem->packetUid, item->size);
+    }
+  else if (PacketPrinter::IsTrailer (uid))
+    {
+      ns3::Buffer::Iterator j = data.End ();
+      j.Prev (data.GetSize () - (offset + item->size));
+      printer.PrintChunk (uid, j, os, extraItem->packetUid, item->size);
+    }
+  else 
+    {
+      NS_ASSERT (false);
+    }
+  return extraItem->fragmentEnd - extraItem->fragmentStart;
+}
+
+uint32_t
+PacketMetadata::GetTotalSize (void) const
+{
+  uint32_t totalSize = 0;
+  uint16_t current = m_head;
+  uint16_t tail = m_tail;
+  while (current != 0xffff)
+    {
+      struct PacketMetadata::SmallItem item;
+      PacketMetadata::ExtraItem extraItem;
+      ReadItems (current, &item, &extraItem);
+      totalSize += extraItem.fragmentEnd - extraItem.fragmentStart;
+      if (current == tail)
+        {
+          break;
+        }
+      NS_ASSERT (current != item.next);
+      current = item.next;
+    }
+  return totalSize;
+}
+
+uint32_t 
+PacketMetadata::GetUid (void) const
+{
+  return m_packetUid;
+}
+
+void
+PacketMetadata::Print (std::ostream &os, Buffer data, const PacketPrinter &printer) const
+{
+  if (!m_enable) 
+    {
+      return;
+    }
+  NS_ASSERT (m_data != 0);
+  NS_ASSERT (GetTotalSize () == data.GetSize ());
+  struct PacketMetadata::SmallItem item;
+  struct PacketMetadata::ExtraItem extraItem;
+  if (printer.m_forward)
+    {
+      uint32_t current = m_head;
+      uint32_t offset = 0;
+      while (current != 0xffff)
+        {
+          ReadItems (current, &item, &extraItem);
+          uint32_t realSize = DoPrint (&item, &extraItem, data, offset, printer, os);
+          offset += realSize;
+          if (current == m_tail)
+            {
+              break;
+            }
+          if (item.next != 0xffff)
+            {
+              os << printer.m_separator;
+            }
+          NS_ASSERT (current != item.next);
+          current = item.next;
+        }
+    }
+  else
+    {
+      uint32_t current = m_tail;
+      uint32_t offset = data.GetSize ();
+      while (current != 0xffff)
+        {
+          ReadItems (current, &item, &extraItem);
+          uint32_t realSize = DoPrint (&item, &extraItem, data, offset - item.size, printer, os);
+          offset -= realSize;
+          if (current == m_head)
+            {
+              break;
+            }
+          if (item.prev != 0xffff)
+            {
+              os << printer.m_separator;
+            }
+          NS_ASSERT (current != item.prev);
+          current = item.prev;
+        }
+    }
+}
+
+
+
+}; // namespace ns3
+
+#include <stdarg.h>
+#include <iostream>
+#include <sstream>
+#include "ns3/test.h"
+#include "header.h"
+#include "trailer.h"
+#include "packet.h"
+
+namespace ns3 {
+
+template <int N>
+class HistoryHeader : public Header
+{
+public:
+  HistoryHeader ();
+  bool IsOk (void) const;
+private:
+  virtual std::string DoGetName (void) const;
+  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);
+  bool m_ok;
+};
+
+template <int N>
+HistoryHeader<N>::HistoryHeader ()
+  : m_ok (false)
+{}
+
+template <int N>
+bool 
+HistoryHeader<N>::IsOk (void) const
+{
+  return m_ok;
+}
+
+template <int N>
+std::string 
+HistoryHeader<N>::DoGetName (void) const
+{
+  std::ostringstream oss;
+  oss << N;
+  return oss.str ();
+}
+
+template <int N>
+void 
+HistoryHeader<N>::PrintTo (std::ostream &os) const
+{
+  NS_ASSERT (false);
+}
+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)
+{
+  m_ok = true;
+  for (int i = 0; i < N; i++)
+    {
+      if (start.ReadU8 () != N)
+        {
+          m_ok = false;
+        }
+    }
+  return N;
+}
+
+template <int N>
+class HistoryTrailer : public Trailer
+{
+public:
+  HistoryTrailer ();
+  bool IsOk (void) const;
+private:
+  virtual std::string DoGetName (void) const;
+  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);
+  bool m_ok;
+};
+
+template <int N>
+HistoryTrailer<N>::HistoryTrailer ()
+  : m_ok (false)
+{}
+
+template <int N>
+bool
+HistoryTrailer<N>::IsOk (void) const
+{
+  return m_ok;
+}
+
+template <int N>
+std::string 
+HistoryTrailer<N>::DoGetName (void) const
+{
+  std::ostringstream oss;
+  oss << N;
+  return oss.str ();
+}
+template <int N>
+void 
+HistoryTrailer<N>::PrintTo (std::ostream &os) const
+{
+  NS_ASSERT (false);
+}
+template <int N>
+uint32_t 
+HistoryTrailer<N>::GetSerializedSize (void) const
+{
+  return N;
+}
+template <int N>
+void 
+HistoryTrailer<N>::SerializeTo (Buffer::Iterator start) const
+{
+  start.Prev (N);
+  start.WriteU8 (N, N);
+}
+template <int N>
+uint32_t
+HistoryTrailer<N>::DeserializeFrom (Buffer::Iterator start)
+{
+  m_ok = true;
+  start.Prev (N);
+  for (int i = 0; i < N; i++)
+    {
+      if (start.ReadU8 () != N)
+        {
+          m_ok = false;
+        }
+    }
+  return N;
+}
+
+
+
+class PacketMetadataTest : public Test {
+public:
+  PacketMetadataTest ();
+  virtual ~PacketMetadataTest ();
+  bool CheckHistory (Packet p, const char *file, int line, uint32_t n, ...);
+  virtual bool RunTests (void);
+private:
+  template <int N>
+  void PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, const HistoryHeader<N> *header);
+  template <int N>
+  void PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, const HistoryTrailer<N> *trailer);
+  void PrintFragment (std::ostream &os,uint32_t packetUid,
+                      uint32_t size,std::string & name, 
+                      struct PacketPrinter::FragmentInformation info);
+  void PrintDefault (std::ostream& os,uint32_t packetUid,
+                     uint32_t size,std::string& name,
+                     struct PacketPrinter::FragmentInformation info);
+  void PrintPayload (std::ostream &os,uint32_t packetUid,
+                     uint32_t size,
+                     struct PacketPrinter::FragmentInformation info);
+  template <int N>
+  void RegisterHeader (void);
+  template <int N>
+  void RegisterTrailer (void);
+  void CleanupPrints (void);
+  bool Check (const char *file, int line, std::list<int> expected);
+
+
+  bool m_headerError;
+  bool m_trailerError;
+  std::list<int> m_prints;
+  PacketPrinter m_printer;
+};
+
+PacketMetadataTest::PacketMetadataTest ()
+  : Test ("PacketMetadata")
+{
+  m_printer.AddPayloadPrinter (MakeCallback (&PacketMetadataTest::PrintPayload, this));
+  m_printer.AddDefaultPrinter (MakeCallback (&PacketMetadataTest::PrintDefault, this));
+}
+
+PacketMetadataTest::~PacketMetadataTest ()
+{}
+
+template <int N>
+void 
+PacketMetadataTest::RegisterHeader (void)
+{
+  static bool registered = false;
+  if (!registered)
+    {
+      m_printer.AddHeaderPrinter (MakeCallback (&PacketMetadataTest::PrintHeader<N>, this),
+                                  MakeCallback (&PacketMetadataTest::PrintFragment, this));
+      registered = true;
+    }
+}
+
+template <int N>
+void 
+PacketMetadataTest::RegisterTrailer (void)
+{
+  static bool registered = false;
+  if (!registered)
+    {
+      m_printer.AddTrailerPrinter (MakeCallback (&PacketMetadataTest::PrintTrailer<N>, this),
+                                   MakeCallback (&PacketMetadataTest::PrintFragment, this));
+      registered = true;
+    }
+}
+
+
+template <int N>
+void 
+PacketMetadataTest::PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, 
+                                const HistoryHeader<N> *header)
+{
+  if (!header->IsOk ())
+    {
+      m_headerError = true;
+    }
+  m_prints.push_back (N);
+}
+
+template <int N>
+void 
+PacketMetadataTest::PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, 
+                                 const HistoryTrailer<N> *trailer)
+{
+  if (!trailer->IsOk ())
+    {
+      m_trailerError = true;
+    }
+  m_prints.push_back (N);
+}
+void 
+PacketMetadataTest::PrintFragment (std::ostream &os,uint32_t packetUid,
+                                  uint32_t size,std::string & name, 
+                                  struct PacketPrinter::FragmentInformation info)
+{
+  m_prints.push_back (size - (info.end + info.start));
+}
+void 
+PacketMetadataTest::PrintDefault (std::ostream& os,uint32_t packetUid,
+                     uint32_t size,std::string& name,
+                     struct PacketPrinter::FragmentInformation info)
+{
+  NS_ASSERT (false);
+}
+void 
+PacketMetadataTest::PrintPayload (std::ostream &os,uint32_t packetUid,
+                                 uint32_t size,
+                                 struct PacketPrinter::FragmentInformation info)
+{
+  m_prints.push_back (size - (info.end + info.start));
+}
+
+
+void 
+PacketMetadataTest::CleanupPrints (void)
+{
+  m_prints.clear ();
+}
+
+bool
+PacketMetadataTest::Check (const char *file, int line, std::list<int> expected)
+{
+  if (m_headerError)
+    {
+      std::cout << "PacketMetadata header error. file=" << file 
+                << ", line=" << line << std::endl;
+      return false;
+    }
+  if (m_trailerError)
+    {
+      std::cout << "PacketMetadata trailer error. file=" << file 
+                << ", line=" << line << std::endl;
+      return false;
+    }
+  if (expected.size () != m_prints.size ())
+    {
+      goto error;
+    }
+  for (std::list<int>::iterator i = m_prints.begin (),
+         j = expected.begin (); 
+       i != m_prints.end (); i++, j++)
+    {
+      NS_ASSERT (j != expected.end ());
+      if (*j != *i)
+        {
+          goto error;
+        }
+    }
+  return true;
+ error:
+  std::cout << "PacketMetadata error. file="<< file 
+            << ", line=" << line << ", got:\"";
+  for (std::list<int>::iterator i = m_prints.begin (); 
+       i != m_prints.end (); i++)
+    {
+      std::cout << *i << ", ";
+    }
+  std::cout << "\", expected: \"";
+  for (std::list<int>::iterator j = expected.begin ();
+       j != expected.end (); j++)
+    {
+      std::cout << *j << ", ";
+    }
+  std::cout << "\"" << std::endl;
+  return false;
+}
+
+bool 
+PacketMetadataTest::CheckHistory (Packet p, const char *file, int line, uint32_t n, ...)
+{
+  m_headerError = false;
+  m_trailerError = false;
+  std::list<int> expected;
+  va_list ap;
+  va_start (ap, n);
+  for (uint32_t j = 0; j < n; j++)
+    {
+      int v = va_arg (ap, int);
+      expected.push_back (v);
+    }
+  va_end (ap);
+
+  m_printer.PrintForward ();
+  p.Print (std::cerr, m_printer);
+  bool ok = Check (file, line, expected);
+  CleanupPrints ();
+  if (!ok)
+    {
+      return false;
+    }
+
+  m_printer.PrintBackward ();
+  p.Print (std::cerr, m_printer);
+  expected.reverse ();
+  ok = Check (file, line, expected);
+  CleanupPrints ();
+  return ok;
+}
+
+#define ADD_HEADER(p, n)                        \
+  {                                             \
+    HistoryHeader<n> header;                    \
+    RegisterHeader<n> ();                       \
+    p.AddHeader (header);                       \
+  }
+#define ADD_TRAILER(p, n)                       \
+  {                                             \
+    HistoryTrailer<n> trailer;                  \
+    RegisterTrailer<n> ();                      \
+    p.AddTrailer (trailer);                     \
+  }
+#define REM_HEADER(p, n)                        \
+  {                                             \
+    HistoryHeader<n> header;                    \
+    RegisterHeader<n> ();                       \
+    p.RemoveHeader (header);                    \
+  }
+#define REM_TRAILER(p, n)                       \
+  {                                             \
+    HistoryTrailer<n> trailer;                  \
+    RegisterTrailer<n> ();                      \
+    p.RemoveTrailer (trailer);                  \
+  }
+#define CHECK_HISTORY(p, ...)                   \
+  {                                             \
+    if (!CheckHistory (p, __FILE__,             \
+                      __LINE__, __VA_ARGS__))   \
+      {                                         \
+        ok = false;                             \
+      }                                         \
+  }
+
+bool
+PacketMetadataTest::RunTests (void)
+{
+  bool ok = true;
+
+  PacketMetadata::Enable ();
+
+  Packet p = Packet (0);
+  Packet p1 = Packet (0);
+
+  p = Packet (10);
+  ADD_TRAILER (p, 100);
+  CHECK_HISTORY (p, 2, 10, 100);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  CHECK_HISTORY (p, 4, 
+                 3, 2, 1, 10);
+  ADD_HEADER (p, 5);
+  CHECK_HISTORY (p, 5, 
+                 5, 3, 2, 1, 10);
+  ADD_HEADER (p, 6);
+  CHECK_HISTORY (p, 6, 
+                 6, 5, 3, 2, 1, 10);
+
+  p = Packet (10);
+  ADD_HEADER (p, 1);
+  ADD_HEADER (p, 2);
+  ADD_HEADER (p, 3);
+  REM_HEADER (p, 3);
+  CHECK_HISTORY (p, 3, 
+                 2, 1, 10);
+
+  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, 2, 
+                 1, 10);
+
+  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, 1, 10);
+
+  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, 1, 10);
+  CHECK_HISTORY (p, 4, 
+                 3, 2, 1, 10);
+  ADD_HEADER (p1, 1);
+  ADD_HEADER (p1, 2);
+  CHECK_HISTORY (p1, 3, 
+                 2, 1, 10);
+  CHECK_HISTORY (p, 4, 
+                 3, 2, 1, 10);
+  ADD_HEADER (p, 3);
+  CHECK_HISTORY (p, 5, 
+                 3, 3, 2, 1, 10);
+  ADD_TRAILER (p, 4);
+  CHECK_HISTORY (p, 6, 
+                 3, 3, 2, 1, 10, 4);
+  ADD_TRAILER (p, 5);
+  CHECK_HISTORY (p, 7, 
+                 3, 3, 2, 1, 10, 4, 5);
+  REM_HEADER (p, 3);
+  CHECK_HISTORY (p, 6, 
+                 3, 2, 1, 10, 4, 5);
+  REM_TRAILER (p, 5);
+  CHECK_HISTORY (p, 5, 
+                 3, 2, 1, 10, 4);
+  p1 = p;
+  REM_TRAILER (p, 4);
+  CHECK_HISTORY (p, 4, 
+                 3, 2, 1, 10);
+  CHECK_HISTORY (p1, 5, 
+                 3, 2, 1, 10, 4);
+  p1.RemoveAtStart (3);
+  CHECK_HISTORY (p1, 4, 
+                 2, 1, 10, 4);
+  p1.RemoveAtStart (1);
+  CHECK_HISTORY (p1, 4, 
+                 1, 1, 10, 4);
+  p1.RemoveAtStart (1);
+  CHECK_HISTORY (p1, 3, 
+                 1, 10, 4);
+  p1.RemoveAtEnd (4);
+  CHECK_HISTORY (p1, 2, 
+                 1, 10);
+  p1.RemoveAtStart (1);
+  CHECK_HISTORY (p1, 1, 10);
+
+  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, 5, 3, 10, 10, 6, 4);
+
+  p = Packet (10);
+  ADD_HEADER (p, 10);
+  ADD_TRAILER (p, 6);
+  p.RemoveAtEnd (18);
+  ADD_TRAILER (p, 5);
+  ADD_HEADER (p, 3);
+  CHECK_HISTORY (p, 3, 3, 8, 5);
+  p.RemoveAtStart (12);
+  CHECK_HISTORY (p, 1, 4);
+  p.RemoveAtEnd (2);
+  CHECK_HISTORY (p, 1, 2);
+  ADD_HEADER (p, 10);
+  CHECK_HISTORY (p, 2, 10, 2);
+  p.RemoveAtEnd (5);
+  CHECK_HISTORY (p, 1, 7);
+
+  Packet p2 = Packet (0);
+  Packet p3 = Packet (0);
+
+  p = Packet (40);
+  ADD_HEADER (p, 5);
+  ADD_HEADER (p, 8);
+  CHECK_HISTORY (p, 3, 8, 5, 40);
+  p1 = p.CreateFragment (0, 5);
+  p2 = p.CreateFragment (5, 5);
+  p3 = p.CreateFragment (10, 43);
+  CHECK_HISTORY (p1, 1, 5);
+  CHECK_HISTORY (p2, 2, 3, 2);
+  CHECK_HISTORY (p3, 2, 3, 40);
+  p1.AddAtEnd (p2);
+  CHECK_HISTORY (p1, 2, 8, 2);
+  CHECK_HISTORY (p2, 2, 3, 2);
+  p1.AddAtEnd (p3);
+  CHECK_HISTORY (p1, 3, 8, 5, 40);
+  CHECK_HISTORY (p2, 2, 3, 2);
+  CHECK_HISTORY (p3, 2, 3, 40);
+  p1 = p.CreateFragment (0, 5);
+  CHECK_HISTORY (p1, 1, 5);
+
+  p3 = Packet (50);
+  ADD_HEADER (p3, 8);
+  CHECK_HISTORY (p3, 2, 8, 50);
+  CHECK_HISTORY (p1, 1, 5);
+  p1.AddAtEnd (p3);
+  CHECK_HISTORY (p1, 3, 5, 8, 50);
+  ADD_HEADER (p1, 5);
+  CHECK_HISTORY (p1, 4, 5, 5, 8, 50);
+  ADD_TRAILER (p1, 2);
+  CHECK_HISTORY (p1, 5, 5, 5, 8, 50, 2);
+  REM_HEADER (p1, 5);
+  CHECK_HISTORY (p1, 4, 5, 8, 50, 2);
+  p1.RemoveAtEnd (60);
+  CHECK_HISTORY (p1, 1, 5);
+  p1.AddAtEnd (p2);
+  CHECK_HISTORY (p1, 2, 8, 2);
+  CHECK_HISTORY (p2, 2, 3, 2);
+
+  p3 = Packet (40);
+  ADD_HEADER (p3, 5);
+  ADD_HEADER (p3, 5);
+  CHECK_HISTORY (p3, 3, 5, 5, 40);
+  p1 = p3.CreateFragment (0, 5);
+  p2 = p3.CreateFragment (5, 5);
+  CHECK_HISTORY (p1, 1, 5);
+  CHECK_HISTORY (p2, 1, 5);
+  p1.AddAtEnd (p2);
+  CHECK_HISTORY (p1, 2, 5, 5);
+
+  p = Packet (0);
+  CHECK_HISTORY (p, 0);
+
+  p3 = Packet (0);
+  ADD_HEADER (p3, 5);
+  ADD_HEADER (p3, 5);
+  CHECK_HISTORY (p3, 2, 5, 5);
+  p1 = p3.CreateFragment (0, 4);
+  p2 = p3.CreateFragment (9, 1);
+  CHECK_HISTORY (p1, 1, 4);
+  CHECK_HISTORY (p2, 1, 1);
+  p1.AddAtEnd (p2);
+  CHECK_HISTORY (p1, 2, 4, 1);
+
+
+  p = Packet (2000);
+  CHECK_HISTORY (p, 1, 2000);
+  
+  p = Packet ();
+  ADD_TRAILER (p, 10);
+  ADD_HEADER (p, 5);
+  p1 = p.CreateFragment (0, 8);
+  p2 = p.CreateFragment (8, 7);
+  p1.AddAtEnd (p2);
+  CHECK_HISTORY (p, 2, 5, 10);
+
+  p = Packet ();
+  ADD_TRAILER (p, 10);
+  REM_TRAILER (p, 10);
+  ADD_TRAILER (p, 10);
+  CHECK_HISTORY (p, 1, 10);
+
+  p = Packet ();
+  ADD_HEADER (p, 10);
+  REM_HEADER (p, 10);
+  ADD_HEADER (p, 10);
+  CHECK_HISTORY (p, 1, 10);
+
+  return ok;
+}
+
+static PacketMetadataTest g_packetHistoryTest;
+
+}//namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/packet-metadata.h	Fri Jul 06 14:39:59 2007 -0700
@@ -0,0 +1,335 @@
+/* -*- 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_METADATA_H
+#define PACKET_METADATA_H
+
+#include <stdint.h>
+#include <vector>
+#include "ns3/callback.h"
+#include "ns3/assert.h"
+#include "packet-printer.h"
+
+namespace ns3 {
+
+class Chunk;
+class Buffer;
+
+/**
+ * \internal
+ * \brief handle packet metadata about packet headers and trailers
+ *
+ * This class is used by the Packet class to record every operation
+ * performed on the packet's buffer. This class also provides
+ * an implementation of the Packet::Print methods which uses
+ * the metadata to analyse the content of the packet's buffer.
+ *
+ * To achieve this, this class maintains a linked list of so-called
+ * "items", each of which represents a header or a trailer, or 
+ * payload, or a fragment of any of these. Each item contains a "next"
+ * and a "prev" field which point to the next and previous entries
+ * in the linked list. The PacketMetadata class maintains a pair
+ * of pointers to the head and the tail of the linked list.
+ *
+ * Each entry in the list also maintains:
+ *   - its native size (the size it had when it was first added
+ *     to the packet)
+ *   - its type: identifies what kind of header, what kind of trailer,
+ *     if it is payload or not
+ *   - the uid of the packet to which it was first added
+ *   - the start and end of the area represented by a fragment
+ *     if it is one.
+ *
+ * This linked list is flattened in a byte buffer stored in
+ * struct PacketMetadata::Data. Each entry of the linked list is
+ * identified by an offset which identifies the first byte of the
+ * entry from the start of the data buffer. The size of this data
+ * buffer is 2^16-1 bytes maximum which somewhat limits the number
+ * of entries which can be stored in this linked list but it is
+ * quite unlikely to hit this limit in practice.
+ *
+ * Each item of the linked list is a variable-sized byte buffer
+ * made of a number of fields. Some of these fields are stored
+ * as fixed-size 32 bit integers, others as fixed-size 16 bit 
+ * integers, and some others as variable-size 32-bit integers.
+ * The variable-size 32 bit integers are stored using the uleb128
+ * encoding.
+ */
+class PacketMetadata {
+public:
+  static void Enable (void);
+  static void SetOptOne (bool optOne);
+
+  inline PacketMetadata (uint32_t uid, uint32_t size);
+  inline PacketMetadata (PacketMetadata const &o);
+  inline PacketMetadata &operator = (PacketMetadata const& o);
+  inline ~PacketMetadata ();
+
+  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);
+
+  PacketMetadata CreateFragment (uint32_t start, uint32_t end) const;
+  void AddAtEnd (PacketMetadata const&o);
+  void AddPaddingAtEnd (uint32_t end);
+  void RemoveAtStart (uint32_t start);
+  void RemoveAtEnd (uint32_t end);
+
+  uint32_t GetUid (void) const;
+
+  void PrintDefault (std::ostream &os, Buffer buffer) const;
+  void Print (std::ostream &os, Buffer buffer, PacketPrinter const &printer) const;
+
+  static void PrintStats (void);
+
+private:
+  struct Data {
+    /* number of references to this struct Data instance. */
+    uint16_t m_count;
+    /* size (in bytes) of m_data buffer below */
+    uint16_t m_size;
+    /* max of the m_used field over all objects which 
+     * reference this struct Data instance */
+    uint16_t m_dirtyEnd;
+    /* variable-sized buffer of bytes */
+    uint8_t m_data[10];
+  };
+  /* Note that since the next and prev fields are 16 bit integers
+     and since the value 0xffff is reserved to identify the 
+     fact that the end or the start of the list is reached,
+     only a limited number of elements can be stored in 
+     a m_data byte buffer.
+   */
+  struct SmallItem {
+    /* offset (in bytes) from start of m_data buffer 
+       to next element in linked list. value is 0xffff 
+       if next element does not exist.
+       stored as a fixed-size 16 bit integer.
+    */
+    uint16_t next;
+    /* offset (in bytes) from start of m_data buffer 
+       to previous element in linked list. value is 0xffff 
+       if previous element does not exist.
+       stored as a fixed-size 16 bit integer.
+     */
+    uint16_t prev;
+    /* the high 31 bits of this field identify the 
+       type of the header or trailer represented by 
+       this item: the value zero represents payload.
+       If the low bit of this uid is one, an ExtraItem
+       structure follows this SmallItem structure.
+       stored as a variable-size 32 bit integer.
+     */
+    uint32_t typeUid;
+    /* the size (in bytes) of the header or trailer represented
+       by this element.
+       stored as a variable-size 32 bit integer.
+     */
+    uint32_t size;
+    /* this field tries to uniquely identify each header or 
+       trailer _instance_ while the typeUid field uniquely
+       identifies each header or trailer _type_. This field
+       is used to test whether two items are equal in the sense 
+       that they represent the same header or trailer instance.
+       That equality test is based on the typeUid and chunkUid
+       fields so, the likelyhood that two header instances 
+       share the same chunkUid _and_ typeUid is very small 
+       unless they are really representations of the same header
+       instance.
+       stored as a fixed-size 16 bit integer.
+     */
+    uint16_t chunkUid;
+  };
+  struct ExtraItem {
+    /* offset (in bytes) from start of original header to 
+       the start of the fragment still present.
+       stored as a variable-size 32 bit integer.
+     */
+    uint32_t fragmentStart;
+    /* offset (in bytes) from start of original header to 
+       the end of the fragment still present.
+       stored as a variable-size 32 bit integer.
+     */
+    uint32_t fragmentEnd;
+    /* the packetUid of the packet in which this header or trailer
+       was first added. It could be different from the m_packetUid
+       field if the user has aggregated multiple packets into one.
+       stored as a fixed-size 32 bit integer.
+     */
+    uint32_t packetUid;
+  };
+
+  typedef std::vector<struct Data *> DataFreeList;
+  
+  PacketMetadata ();
+  void DoAddHeader (uint32_t uid, uint32_t size);
+  void DoRemoveHeader (uint32_t uid, uint32_t size);
+  void DoAddTrailer (uint32_t uid, uint32_t size);
+  void DoRemoveTrailer (uint32_t uid, uint32_t size);
+
+  inline uint16_t AddSmall (const PacketMetadata::SmallItem *item);
+  uint16_t AddBig (uint32_t head, uint32_t tail,
+                   const PacketMetadata::SmallItem *item, 
+                   const PacketMetadata::ExtraItem *extraItem);
+  void ReplaceTail (PacketMetadata::SmallItem *item, 
+                    PacketMetadata::ExtraItem *extraItem,
+                    uint32_t available);
+  inline void UpdateHead (uint16_t written);
+  inline void UpdateTail (uint16_t written);
+  uint32_t GetUleb128Size (uint32_t value) const;
+  uint32_t ReadUleb128 (const uint8_t **pBuffer) const;
+  inline void Append16 (uint16_t value, uint8_t *buffer);
+  inline bool TryToAppend (uint32_t value, uint8_t **pBuffer, uint8_t *end);
+  inline bool TryToAppendFast (uint32_t value, uint8_t **pBuffer, uint8_t *end);
+  inline bool TryToAppend32 (uint32_t value, uint8_t **pBuffer, uint8_t *end);
+  inline bool TryToAppend16 (uint16_t value, uint8_t **pBuffer, uint8_t *end);
+  void AppendValue (uint32_t value, uint8_t *buffer);
+  void AppendValueExtra (uint32_t value, uint8_t *buffer);
+  inline void Reserve (uint32_t n);
+  void ReserveCopy (uint32_t n);
+  uint32_t DoPrint (const struct PacketMetadata::SmallItem *item,
+                    const struct PacketMetadata::ExtraItem *extraItem,
+                    Buffer data, uint32_t offset, const PacketPrinter &printer,
+                    std::ostream &os) const;
+  uint32_t GetTotalSize (void) const;
+  uint32_t ReadItems (uint16_t current, 
+                      struct PacketMetadata::SmallItem *item,
+                      struct PacketMetadata::ExtraItem *extraItem) const;
+
+
+  static struct PacketMetadata::Data *Create (uint32_t size);
+  static void Recycle (struct PacketMetadata::Data *data);
+  static struct PacketMetadata::Data *Allocate (uint32_t n);
+  static void Deallocate (struct PacketMetadata::Data *data);
+  
+  static DataFreeList m_freeList;
+  static bool m_enable;
+  static uint32_t m_maxSize;
+  static uint16_t m_chunkUid;
+  
+  struct Data *m_data;
+  /**
+     head -(next)-> tail
+       ^             |
+        \---(prev)---|
+   */
+  uint16_t m_head;
+  uint16_t m_tail;
+  uint16_t m_used;
+  uint32_t m_packetUid;
+};
+
+}; // namespace ns3
+
+namespace ns3 {
+
+template <typename T>
+void 
+PacketMetadata::AddHeader (T const &header, uint32_t size)
+{
+  DoAddHeader (PacketPrinter::GetHeaderUid<T> (), size);
+}
+
+template <typename T>
+void 
+PacketMetadata::RemoveHeader (T const &header, uint32_t size)
+{
+  DoRemoveHeader (PacketPrinter::GetHeaderUid<T> (), size);
+}
+template <typename T>
+void 
+PacketMetadata::AddTrailer (T const &trailer, uint32_t size)
+{
+  DoAddTrailer (PacketPrinter::GetTrailerUid<T> (), size);
+}
+template <typename T>
+void 
+PacketMetadata::RemoveTrailer (T const &trailer, uint32_t size)
+{
+  DoRemoveTrailer (PacketPrinter::GetTrailerUid<T> (), size);
+}
+
+
+PacketMetadata::PacketMetadata (uint32_t uid, uint32_t size)
+  : m_data (m_data = PacketMetadata::Create (10)),
+    m_head (0xffff),
+    m_tail (0xffff),
+    m_used (0),
+    m_packetUid (uid)
+{
+  memset (m_data->m_data, 0xff, 4);
+  if (size > 0)
+    {
+      DoAddHeader (0, size);
+    }
+}
+PacketMetadata::PacketMetadata (PacketMetadata const &o)
+  : m_data (o.m_data),
+    m_head (o.m_head),
+    m_tail (o.m_tail),
+    m_used (o.m_used),
+    m_packetUid (o.m_packetUid)
+{
+  NS_ASSERT (m_data != 0);
+  m_data->m_count++;
+}
+PacketMetadata &
+PacketMetadata::operator = (PacketMetadata const& o)
+{
+  if (m_data == o.m_data) 
+    {
+      // self assignment
+      return *this;
+    }
+  NS_ASSERT (m_data != 0);
+  m_data->m_count--;
+  if (m_data->m_count == 0) 
+    {
+      PacketMetadata::Recycle (m_data);
+    }
+  m_data = o.m_data;
+  m_head = o.m_head;
+  m_tail = o.m_tail;
+  m_used = o.m_used;
+  m_packetUid = o.m_packetUid;
+  NS_ASSERT (m_data != 0);
+  m_data->m_count++;
+  return *this;
+}
+PacketMetadata::~PacketMetadata ()
+{
+  NS_ASSERT (m_data != 0);
+  m_data->m_count--;
+  if (m_data->m_count == 0) 
+    {
+      PacketMetadata::Recycle (m_data);
+    }
+}
+
+}; // namespace ns3
+
+
+#endif /* PACKET_METADATA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/packet-printer.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -0,0 +1,235 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 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 "packet-printer.h"
+
+namespace ns3 {
+
+PacketPrinter::PacketPrinter ()
+  : m_forward (true),
+    m_separator ("")
+{}
+
+void 
+PacketPrinter::PrintForward (void)
+{
+  m_forward = true;
+}
+void 
+PacketPrinter::PrintBackward (void)
+{
+  m_forward = false;
+}
+void
+PacketPrinter::SetSeparator (std::string separator)
+{
+  m_separator = separator;
+}
+void 
+PacketPrinter::AddPayloadPrinter (PayloadPrinter printer)
+{
+  m_payloadPrinter = printer;
+}
+void 
+PacketPrinter::AddDefaultPrinter (DefaultPrinter printer)
+{
+  m_defaultPrinter = printer;
+}
+
+PacketPrinter::RegisteredChunks *
+PacketPrinter::GetRegisteredChunks (void)
+{
+  static RegisteredChunks registeredChunks;
+  return &registeredChunks;
+}
+
+PacketPrinter 
+PacketPrinter::GetDefault (void)
+{
+  return *(PacketPrinter::PeekDefault ());
+}
+PacketPrinter *
+PacketPrinter::PeekDefault (void)
+{
+  static PacketPrinter *tmp = PacketPrinter::CreateStaticDefault ();
+  return tmp;
+}
+PacketPrinter *
+PacketPrinter::CreateStaticDefault (void)
+{
+  static PacketPrinter tmp;
+  tmp.PrintForward ();
+  tmp.AddPayloadPrinter (MakeCallback (&PacketPrinter::DoDefaultPrintPayload));
+  tmp.SetSeparator (" ");
+  return &tmp;
+}
+
+
+void 
+PacketPrinter::PrintChunk (uint32_t chunkUid, 
+                           Buffer::Iterator start, 
+                           std::ostream &os, 
+                           uint32_t packetUid,
+                           uint32_t size) const
+{
+  RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks ();
+  NS_ASSERT (chunkUid >= 1 && chunkUid/2 <= registeredChunks->size ());
+  for (PrinterList::const_iterator i = m_printerList.begin (); i != m_printerList.end (); i++)
+    {
+      if (i->m_chunkUid == chunkUid)
+        {
+          DoPrintCallback cb = (*registeredChunks)[chunkUid/2-1].printCallback;
+          cb (i->m_printer, start, os, packetUid, size);
+          return;
+        }
+    }
+  DoGetNameCallback cb = (*registeredChunks)[chunkUid/2-1].getNameCallback;
+  std::string name = cb ();
+  struct PacketPrinter::FragmentInformation info;
+  info.start = 0;
+  info.end = 0;
+  if (!m_defaultPrinter.IsNull ())
+    {
+      m_defaultPrinter (os, packetUid, size, name, info);
+    }
+}
+void 
+PacketPrinter::PrintChunkFragment (uint32_t chunkUid,
+                                   std::ostream &os,
+                                   uint32_t packetUid,
+                                   uint32_t size,
+                                   uint32_t fragmentStart,
+                                   uint32_t fragmentEnd) const
+{
+  RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks ();
+  NS_ASSERT (chunkUid >= 1 && chunkUid/2 <= registeredChunks->size ());
+  DoGetNameCallback cb = (*registeredChunks)[chunkUid/2-1].getNameCallback;
+  std::string name = cb ();
+  struct PacketPrinter::FragmentInformation info;
+  info.start = fragmentStart;
+  info.end = fragmentEnd;
+  for (PrinterList::const_iterator i = m_printerList.begin (); i != m_printerList.end (); i++)
+    {
+      if (i->m_chunkUid == chunkUid)
+        {
+          i->m_fragmentPrinter (os, packetUid, size, name, info);
+          return;
+        }
+    }
+  if (!m_defaultPrinter.IsNull ())
+    {
+      m_defaultPrinter (os, packetUid, size, name, info);
+    }
+}
+void 
+PacketPrinter::PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size,
+                             uint32_t fragmentStart, uint32_t fragmentEnd) const
+{
+  struct PacketPrinter::FragmentInformation info;
+  info.start = fragmentStart;
+  info.end = fragmentEnd;
+  if (!m_payloadPrinter.IsNull ())
+    {
+      m_payloadPrinter (os, packetUid, size, info);
+    }
+}
+
+void 
+PacketPrinter::DoDefaultPrintPayload (std::ostream & os,
+                                      uint32_t packetUid,
+                                      uint32_t size,
+                                      struct PacketPrinter::FragmentInformation info)
+{
+  os << "DATA ("
+     << "length " << size - (info.end + info.start);
+  if (info.start != 0 || info.end != 0)
+    {
+      os << " "
+         << "trim_start " << info.start << " "
+         << "trim_end " << info.end;
+    }
+  os << ")";
+}
+
+void 
+PacketPrinter::DoDefaultPrintDefault (std::ostream & os,
+                                      uint32_t packetUid,
+                                      uint32_t size,
+                                      std::string &name,
+                                      struct PacketPrinter::FragmentInformation info)
+{
+  NS_ASSERT_MSG (false, "This should never happen because we provide a printer for _all_ chunk types.");
+}
+
+void 
+PacketPrinter::DoDefaultPrintFragment (std::ostream & os,
+                                       uint32_t packetUid,
+                                       uint32_t size,
+                                       std::string &name,
+                                       struct PacketPrinter::FragmentInformation info)
+{
+  NS_ASSERT (info.start != 0 || info.end != 0);
+  os << name << " "
+     << "("
+     << "length " << size - (info.end + info.start) << " "
+     << "trim_start " << info.start << " "
+     << "trim_end " << info.end
+     << ")"
+    ;
+}
+
+void
+PacketPrinter::DoAddPrinter (uint32_t uid,
+                             Ptr<CallbackImplBase> printer,
+                             Callback<void,
+                             std::ostream &, 
+                             uint32_t, 
+                             uint32_t, 
+                             std::string &,
+                             struct PacketPrinter::FragmentInformation> fragmentPrinter)
+{
+  struct PacketPrinter::Printer p;
+  p.m_chunkUid = uid;
+  p.m_printer = printer;
+  p.m_fragmentPrinter = fragmentPrinter;
+  m_printerList.push_back (p);
+}
+
+bool 
+PacketPrinter::IsTrailer (uint32_t uid)
+{
+  RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks ();
+  NS_ASSERT (uid >= 1 && uid/2 <= registeredChunks->size ());
+  bool isHeader = (*registeredChunks)[uid/2-1].isHeader;
+  return !isHeader;
+}
+bool 
+PacketPrinter::IsHeader (uint32_t uid)
+{
+  RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks ();
+  NS_ASSERT (uid >= 1 && uid/2 <= registeredChunks->size ());
+  bool isHeader = (*registeredChunks)[uid/2-1].isHeader;
+  return isHeader;
+}
+
+
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/packet-printer.h	Fri Jul 06 14:39:59 2007 -0700
@@ -0,0 +1,318 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 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_PRINTER_H
+#define PACKET_PRINTER_H
+
+#include "ns3/callback.h"
+#include "ns3/ptr.h"
+#include "buffer.h"
+#include <vector>
+
+namespace {
+  class ItemList;
+}
+
+namespace ns3 {
+
+class Chunk;
+
+/**
+ * \brief hold a list of print callbacks for packet headers and trailers
+ *
+ * Users can register in instances of this class print callbacks
+ * which are used by Packet::Print to print the content of a packet.
+ */
+class PacketPrinter 
+{
+public:
+  /**
+   * \brief indicates how many bytes were trimmed from a header
+   * or a trailer.
+   */
+  struct FragmentInformation
+  {
+    /**
+     * The number of bytes trimmed from the start of the header or the trailer.
+     */
+    uint32_t start;
+    /**
+     * The number of bytes trimmed from the end of the header or the trailer.
+     */
+    uint32_t end;
+  };
+  /**
+   * \brief callback to print payload.
+   *
+   * Arguments: output stream, packet uid, size, fragment information
+   */
+  typedef Callback<void,std::ostream &,uint32_t,uint32_t,struct PacketPrinter::FragmentInformation>
+    PayloadPrinter;
+
+  /**
+   * \brief callback to print fragmented chunks.
+   *
+   * Arguments: output stream, packet uid, size, header/trailer name, fragment information
+   */
+  typedef Callback<void,std::ostream &,uint32_t,uint32_t,std::string &,struct PacketPrinter::FragmentInformation>
+    ChunkFragmentPrinter;
+
+  /**
+   * \brief callback to print chunks for which no specific callback was specified.
+   *
+   * Arguments: output stream, packet uid, size, header/trailer name, fragment information
+   */
+  typedef Callback<void,std::ostream&,uint32_t,uint32_t,std::string&,struct PacketPrinter::FragmentInformation> 
+    DefaultPrinter;
+
+  PacketPrinter ();
+
+  /**
+   * Print the content of the packet forward.
+   */
+  void PrintForward (void);
+  /**
+   * Print the content of the packet backward.
+   */
+  void PrintBackward (void);
+  void SetSeparator (std::string separator);
+  /**
+   * \param printer printer for payload
+   */
+  void AddPayloadPrinter (PayloadPrinter printer);
+  /**
+   * \param printer printer for the specified chunk
+   * \param fragmentPrinter printer for a fragment of the specified chunk
+   *
+   * If the user has not specified a callback for a specific header present
+   * in a packet, the "default" callback is invoked. If no such callback
+   * was specified, nothing happens.
+   */
+  template <typename T>
+  void AddHeaderPrinter (Callback<void,std::ostream &,uint32_t,uint32_t,const T *> printer,
+                         ChunkFragmentPrinter fragmentPrinter);
+  /**
+   * \param printer printer for the specified chunk
+   * \param fragmentPrinter printer for a fragment of the specified chunk
+   *
+   * If the user has not specified a callback for a specific trailer present
+   * in a packet, the "default" callback is invoked. If no such callback
+   * was specified, nothing happens.
+   */
+  template <typename T>
+  void AddTrailerPrinter (Callback<void,std::ostream &,uint32_t,uint32_t,const T *> printer,
+                          ChunkFragmentPrinter fragmentPrinter);
+  /**
+   * \param printer printer for a chunk for which no callback was specified explicitely
+   */
+  void AddDefaultPrinter (DefaultPrinter printer);
+  
+private:
+  friend class PacketMetadata;
+  typedef void (*DoPrintCallback) (Ptr<CallbackImplBase>, Buffer::Iterator, std::ostream &,
+                                   uint32_t, uint32_t);
+  typedef std::string (*DoGetNameCallback) (void);
+  struct Printer
+  {
+    uint32_t m_chunkUid;
+    Ptr<CallbackImplBase> m_printer;
+    Callback<void,std::ostream &,uint32_t,uint32_t,std::string &,struct PacketPrinter::FragmentInformation>
+      m_fragmentPrinter;
+  };
+  struct RegisteredChunk
+  {
+    DoPrintCallback printCallback;
+    DoGetNameCallback getNameCallback;
+    bool isHeader;
+  };
+  typedef std::vector<struct PacketPrinter::Printer> PrinterList;
+  typedef std::vector<struct RegisteredChunk> RegisteredChunks;
+
+
+  static PacketPrinter GetDefault (void);
+  static PacketPrinter *PeekDefault (void);
+  static PacketPrinter *CreateStaticDefault (void);
+  static void DoDefaultPrintPayload (std::ostream & os,
+                                     uint32_t packetUid,
+                                     uint32_t size,
+                                     struct PacketPrinter::FragmentInformation info);
+  static void DoDefaultPrintDefault (std::ostream & os,
+                                     uint32_t packetUid,
+                                     uint32_t size,
+                                     std::string &name,
+                                     struct PacketPrinter::FragmentInformation info);
+  template <typename T>
+  static void DoDefaultPrint (std::ostream &os, uint32_t packetUid, uint32_t size, const T *chunk);
+  static void DoDefaultPrintFragment (std::ostream & os,
+                                      uint32_t packetUid,
+                                      uint32_t size,
+                                      std::string &name,
+                                      struct PacketPrinter::FragmentInformation info);
+
+  template <typename T>
+  static void DoPrint (Ptr<CallbackImplBase> callbackPrinter,
+                       Buffer::Iterator i, 
+                       std::ostream &os, 
+                       uint32_t packetUid,
+                       uint32_t size);
+  template <typename T>
+  static std::string DoGetName (void);
+  template <typename T>
+  static uint32_t GetTrailerUid (void);
+  template <typename T>
+  static uint32_t GetHeaderUid (void);
+  template <typename T>
+  static uint32_t AllocateUid (bool isHeader);
+  static RegisteredChunks *GetRegisteredChunks (void);
+  static bool IsTrailer (uint32_t uid);
+  static bool IsHeader (uint32_t uid);
+
+
+  void PrintChunk (uint32_t uid, 
+		   Buffer::Iterator i, 
+		   std::ostream &os, 
+		   uint32_t packetUid,
+		   uint32_t size) const;
+  void PrintChunkFragment (uint32_t uid,
+			   std::ostream &os,
+			   uint32_t packetUid,
+			   uint32_t size,
+			   uint32_t fragmentStart,
+			   uint32_t fragmentEnd) const;
+  void PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size,
+		     uint32_t fragmentStart, uint32_t fragmentEnd) const;
+  void DoAddPrinter (uint32_t uid,
+                     Ptr<CallbackImplBase> printer,
+                     Callback<void,
+                       std::ostream &, 
+                       uint32_t, 
+                       uint32_t, 
+                       std::string &,
+                       struct PacketPrinter::FragmentInformation> fragmentPrinter);
+
+  static PacketPrinter m_defaultPacketPrinter;
+  PrinterList m_printerList;
+  PayloadPrinter m_payloadPrinter;
+  DefaultPrinter m_defaultPrinter;
+  bool m_forward;
+  std::string m_separator;
+};
+
+} // namespace ns3
+
+namespace ns3 {
+
+template <typename T>
+void 
+PacketPrinter::AddHeaderPrinter (Callback<void,std::ostream &, uint32_t, uint32_t, const T *> printer,
+			   Callback<void,
+		            std::ostream &, 
+		            uint32_t, 
+                            uint32_t, 
+                            std::string &,
+		            struct PacketPrinter::FragmentInformation> fragmentPrinter)
+{
+  static uint32_t uid = PacketPrinter::GetHeaderUid<T> ();
+  DoAddPrinter (uid, printer.GetImpl (), fragmentPrinter);
+}
+
+template <typename T>
+void 
+PacketPrinter::AddTrailerPrinter (Callback<void,std::ostream &, uint32_t, uint32_t, const T *> printer,
+			   Callback<void,
+		            std::ostream &, 
+		            uint32_t, 
+                            uint32_t, 
+                            std::string &,
+		            struct PacketPrinter::FragmentInformation> fragmentPrinter)
+{
+  static uint32_t uid = PacketPrinter::GetTrailerUid<T> ();
+  DoAddPrinter (uid, printer.GetImpl (), fragmentPrinter);
+}
+
+template <typename T>
+void
+PacketPrinter::DoPrint (Ptr<CallbackImplBase> printerCallback,
+                        Buffer::Iterator i,
+                        std::ostream &os, 
+                        uint32_t packetUid,
+                        uint32_t size)
+{
+  T chunk = T ();
+  chunk.Deserialize (i);
+  Callback<void,std::ostream&,uint32_t,uint32_t,const T*> callback;
+  callback.Assign (printerCallback);
+  callback (os, packetUid, size, &chunk);
+}
+
+template <typename T>
+std::string
+PacketPrinter::DoGetName (void)
+{
+  T chunk = T ();
+  return chunk.GetName ();
+}
+
+template <typename T>
+uint32_t 
+PacketPrinter::GetHeaderUid (void)
+{
+  static uint32_t uid = PacketPrinter::AllocateUid<T> (true);
+  return uid;
+}
+
+template <typename T>
+uint32_t 
+PacketPrinter::GetTrailerUid (void)
+{
+  static uint32_t uid = PacketPrinter::AllocateUid<T> (false);
+  return uid;
+}
+
+template <typename T>
+uint32_t
+PacketPrinter::AllocateUid (bool isHeader)
+{
+  RegisteredChunks *chunks = PacketPrinter::GetRegisteredChunks ();
+  RegisteredChunk chunk;
+  chunk.printCallback = &PacketPrinter::DoPrint<T>;
+  chunk.getNameCallback = &PacketPrinter::DoGetName<T>;
+  chunk.isHeader = isHeader;
+  chunks->push_back (chunk);
+  uint32_t uid = chunks->size () * 2;
+  PacketPrinter::PeekDefault ()->DoAddPrinter (uid, 
+                                               MakeCallback (&PacketPrinter::DoDefaultPrint<T>).GetImpl (),
+                                               MakeCallback (&PacketPrinter::DoDefaultPrintFragment));
+  return uid;
+}
+
+template <typename T>
+void 
+PacketPrinter::DoDefaultPrint (std::ostream &os, uint32_t packetUid, uint32_t size, const T *chunk)
+{
+  os << chunk->GetName () << " ";
+  chunk->Print (os);
+}
+
+
+
+} // namespace ns3
+
+#endif /* PACKET_PRINTER_H */
--- a/src/common/packet.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/packet.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -19,6 +19,7 @@
  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
  */
 #include "packet.h"
+#include "packet-printer.h"
 #include "ns3/assert.h"
 
 namespace ns3 {
@@ -27,20 +28,20 @@
 
 Packet::Packet ()
   : m_buffer (),
-    m_uid (m_globalUid)
+    m_metadata (m_globalUid, 0)
 {
   m_globalUid++;
 }
 
 Packet::Packet (uint32_t size)
   : m_buffer (size),
-    m_uid (m_globalUid)
+    m_metadata (m_globalUid, size)
 {
   m_globalUid++;
 }
 Packet::Packet (uint8_t const*buffer, uint32_t size)
   : m_buffer (),
-    m_uid (m_globalUid)
+    m_metadata (m_globalUid, size)
 {
   m_globalUid++;
   m_buffer.AddAtStart (size);
@@ -48,17 +49,20 @@
   i.Write (buffer, size);
 }
 
-Packet::Packet (Buffer buffer, Tags tags, uint32_t uid)
+Packet::Packet (Buffer buffer, Tags tags, PacketMetadata metadata)
   : m_buffer (buffer),
     m_tags (tags),
-    m_uid (uid)
+    m_metadata (metadata)
 {}
 
 Packet 
 Packet::CreateFragment (uint32_t start, uint32_t length) const
 {
   Buffer buffer = m_buffer.CreateFragment (start, length);
-  return Packet (buffer, m_tags, m_uid);
+  NS_ASSERT (m_buffer.GetSize () >= start + length);
+  uint32_t end = m_buffer.GetSize () - (start + length);
+  PacketMetadata metadata = m_metadata.CreateFragment (start, end);
+  return Packet (buffer, m_tags, metadata);
 }
 
 uint32_t 
@@ -70,6 +74,9 @@
 void 
 Packet::AddAtEnd (Packet packet)
 {
+  packet.m_buffer.TransformIntoRealBuffer ();
+  m_buffer.TransformIntoRealBuffer ();
+
   Buffer src = packet.m_buffer;
   m_buffer.AddAtEnd (src.GetSize ());
   Buffer::Iterator destStart = m_buffer.End ();
@@ -79,21 +86,25 @@
    * XXX: we might need to merge the tag list of the
    * other packet into the current packet.
    */
+  m_metadata.AddAtEnd (packet.m_metadata);
 }
 void
 Packet::AddPaddingAtEnd (uint32_t size)
 {
   m_buffer.AddAtEnd (size);
+  m_metadata.AddPaddingAtEnd (size);
 }
 void 
 Packet::RemoveAtEnd (uint32_t size)
 {
   m_buffer.RemoveAtEnd (size);
+  m_metadata.RemoveAtEnd (size);
 }
 void 
 Packet::RemoveAtStart (uint32_t size)
 {
   m_buffer.RemoveAtStart (size);
+  m_metadata.RemoveAtStart (size);
 }
 
 void 
@@ -111,12 +122,26 @@
 uint32_t 
 Packet::GetUid (void) const
 {
-  return m_uid;
+  return m_metadata.GetUid ();
 }
 
 void 
 Packet::Print (std::ostream &os) const
-{}
+{
+  m_metadata.PrintDefault (os, m_buffer);
+}
+
+void 
+Packet::Print (std::ostream &os, const PacketPrinter &printer) const
+{
+  m_metadata.Print (os, m_buffer, printer);
+}
+
+void
+Packet::EnableMetadata (void)
+{
+  PacketMetadata::Enable ();
+}
 
 }; // namespace ns3
 
--- a/src/common/packet.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/packet.h	Fri Jul 06 14:39:59 2007 -0700
@@ -26,11 +26,14 @@
 #include "header.h"
 #include "trailer.h"
 #include "tags.h"
+#include "packet-metadata.h"
 #include "ns3/callback.h"
 #include "ns3/assert.h"
 
 namespace ns3 {
 
+class PacketPrinter;
+
 /**
  * \brief network packets
  *
@@ -129,7 +132,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 +142,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 +151,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 +161,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.
@@ -254,12 +255,50 @@
    */
   uint32_t GetUid (void) const;
 
+  /**
+   * \param os output stream in which the data should be printed.
+   *
+   * Iterate over the headers and trailers present in this packet, 
+   * from the first header to the last trailer and invoke, for
+   * each of them, the user-provided method Header::DoPrint or 
+   * Trailer::DoPrint methods.
+   */
   void Print (std::ostream &os) const;
+  /**
+   * \param os output stream in which the data should be printed.
+   * \param printer the output formatter to use to print
+   *        the content of this packet.
+   *
+   * Iterate over the headers and trailers present in this packet,
+   * either in the "forward" (first header to last trailer) or in
+   * the "backward" (last trailer to first header) direction, as
+   * specified by the PacketPrinter::PrintForward or the
+   * PacketPrinter::PrintBackward methods. For each header, trailer,
+   * or fragment of a header or a trailer, invoke the user-specified
+   * print callback stored in the specified PacketPrinter.
+   */
+  void Print (std::ostream &os, const PacketPrinter &printer) const;
+
+  /**
+   * By default, packets do not keep around enough metadata to
+   * perform the operations requested by the Print methods. If you
+   * want to be able to invoke any of the two ::Print methods, 
+   * you need to invoke this method at least once during the 
+   * simulation setup and before any packet is created.
+   *
+   * The packet metadata is also used to perform extensive
+   * sanity checks at runtime when performing operations on a 
+   * Packet. For example, this metadata is used to verify that
+   * when you remove a header from a packet, this same header
+   * was actually present at the front of the packet. These
+   * errors will be detected and will abort the program.
+   */
+  static void EnableMetadata (void);
 private:
-  Packet (Buffer buffer, Tags tags, uint32_t uid);
+  Packet (Buffer buffer, Tags tags, PacketMetadata metadata);
   Buffer m_buffer;
   Tags m_tags;
-  uint32_t m_uid;
+  PacketMetadata m_metadata;
   static uint32_t m_globalUid;
 };
 
@@ -282,6 +321,7 @@
   uint32_t size = header.GetSize ();
   m_buffer.AddAtStart (size);
   header.Serialize (m_buffer.Begin ());
+  m_metadata.AddHeader (header, size);
 }
 template <typename T>
 uint32_t
@@ -291,6 +331,7 @@
                  "Must pass Header subclass to Packet::RemoveHeader");
   uint32_t deserialized = header.Deserialize (m_buffer.Begin ());
   m_buffer.RemoveAtStart (deserialized);
+  m_metadata.RemoveHeader (header, deserialized);
   return deserialized;
 }
 template <typename T>
@@ -303,6 +344,7 @@
   m_buffer.AddAtEnd (size);
   Buffer::Iterator end = m_buffer.End ();
   trailer.Serialize (end);
+  m_metadata.AddTrailer (trailer, size);
 }
 template <typename T>
 uint32_t
@@ -312,6 +354,7 @@
                  "Must pass Trailer subclass to Packet::RemoveTrailer");
   uint32_t deserialized = trailer.Deserialize (m_buffer.End ());
   m_buffer.RemoveAtEnd (deserialized);
+  m_metadata.RemoveTrailer (trailer, deserialized);
   return deserialized;
 }
 
--- a/src/common/trailer.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/trailer.h	Fri Jul 06 14:39:59 2007 -0700
@@ -68,8 +68,25 @@
   virtual ~Trailer ();
 private:
   /**
+   * \returns a user-readable name to identify this type of header.
+   *
+   * The string returned is expected to be a single word with 
+   * all capital letters
+   */
+  virtual std::string DoGetName (void) const = 0;
+  /**
    * \param os the std output stream in which this 
    *       protocol trailer must print itself.
+   *
+   * Although the header is free to format its output as it
+   * wishes, it is recommended to follow a few rules to integrate
+   * with the packet pretty printer:
+   *   - start with flags, small field values located between a
+   *     pair of parens. Values should be separated by whitespace.
+   *   - follow the parens with the important fields, separated by
+   *     whitespace.
+   * i.e.:
+   * (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5
    */
   virtual void PrintTo (std::ostream &os) const = 0;
 
--- a/src/common/wscript	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/common/wscript	Fri Jul 06 14:39:59 2007 -0700
@@ -10,9 +10,11 @@
     common.uselib_local = ['ns3-core', 'ns3-simulator']
     common.source = [
         'buffer.cc',
+        'chunk.cc',
         'header.cc',
-        'chunk.cc',
         'trailer.cc',
+        'packet-printer.cc',
+        'packet-metadata.cc',
         'packet.cc',
         'tags.cc',
         'pcap-writer.cc',
@@ -28,11 +30,13 @@
     headers = bld.create_obj('ns3header')
     headers.source = [
         'buffer.h',
+        'chunk.h',
         'header.h',
-        'chunk.h',
         'trailer.h',
         'tags.h',
         'packet.h',
+        'packet-printer.h',
+        'packet-metadata.h',
         'uv-trace-source.h',
         'sv-trace-source.h',
         'fv-trace-source.h',
--- a/src/core/callback.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/core/callback.h	Fri Jul 06 14:39:59 2007 -0700
@@ -225,6 +225,7 @@
 public:
   virtual ~CallbackBase () {}
   virtual CallbackImplBase *PeekImpl (void) const = 0;
+  virtual Ptr<CallbackImplBase> GetImpl (void) const = 0;
 };
 
 /**
@@ -278,7 +279,7 @@
       : m_impl (impl)
   {}
 
-  bool IsNull (void) {
+  bool IsNull (void) const {
     return (PeekImpl () == 0)?true:false;
   }
   void Nullify (void) {
@@ -330,6 +331,19 @@
     const Callback<R, T1,T2,T3,T4,T5> *goodType = static_cast<const Callback<R,T1,T2,T3,T4,T5> *> (&other);
     *this = *goodType;
   }
+  void Assign (Ptr<CallbackImplBase> other) {
+    CallbackImpl<R,T1,T2,T3,T4,T5> *impl = dynamic_cast<CallbackImpl<R,T1,T2,T3,T4,T5> *> (PeekPointer (other));
+    if (other == 0)
+      {
+        NS_FATAL_ERROR ("Incompatible types. (feed to \"c++filt -t\")"
+                        " got=" << typeid (other).name () << 
+                        ", expected=" << typeid (*impl).name ());
+      }
+    *this = Callback<R,T1,T2,T3,T4,T5> (impl);
+  }
+  virtual Ptr<CallbackImplBase>GetImpl (void) const {
+    return m_impl;
+  }
 private:
   virtual CallbackImpl<R,T1,T2,T3,T4,T5> *PeekImpl (void) const {
     return PeekPointer (m_impl);
--- a/src/internet-node/arp-header.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/arp-header.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -83,24 +83,36 @@
   return m_ipv4Dest;
 }
 
+std::string 
+ArpHeader::DoGetName (void) const
+{
+  return "ARP";
+}
 
 void 
 ArpHeader::PrintTo (std::ostream &os) const
 {
-  os << "(arp)";
   if (IsRequest ()) 
     {
-      os << " source mac: " << m_macSource
-          << " source ipv4: " << m_ipv4Source
-          << " dest ipv4: " << m_ipv4Dest;
+      os << "("
+         << "request "
+         << "source mac: " << m_macSource << " "
+         << "source ipv4: " << m_ipv4Source << " "
+         << "dest ipv4: " << m_ipv4Dest
+         << ")"
+        ;
     } 
   else 
     {
       NS_ASSERT (IsReply ());
-      os << " source mac: " << m_macSource
-          << " source ipv4: " << m_ipv4Source
-          << " dest mac: " << m_macDest
-          << " dest ipv4: " <<m_ipv4Dest;
+      os << "("
+         << "reply " 
+         << "source mac: " << m_macSource << " "
+         << "source ipv4: " << m_ipv4Source << " "
+         << "dest mac: " << m_macDest << " "
+         << "dest ipv4: " <<m_ipv4Dest
+         << ")"
+        ;
     }
 }
 uint32_t 
--- a/src/internet-node/arp-header.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/arp-header.h	Fri Jul 06 14:39:59 2007 -0700
@@ -50,6 +50,7 @@
   Ipv4Address GetDestinationIpv4Address (void);
 
 private:
+  virtual std::string DoGetName (void) const;
   /**
    * \param os
    */
--- a/src/internet-node/ascii-trace.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/ascii-trace.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -46,12 +46,14 @@
 void
 AsciiTrace::TraceAllQueues (void)
 {
+  Packet::EnableMetadata ();
   TraceRoot::Connect ("/nodes/*/ipv4/interfaces/*/netdevice/queue/*",
                       MakeCallback (&AsciiTrace::LogDevQueue, this));
 }
 void
 AsciiTrace::TraceAllNetDeviceRx (void)
 {
+  Packet::EnableMetadata ();
   TraceRoot::Connect ("/nodes/*/ipv4/interfaces/*/netdevice/rx",
                       MakeCallback (&AsciiTrace::LogDevRx, this));
 }
@@ -115,7 +117,7 @@
   context.Get (interfaceIndex);
   m_os << "interface=" << interfaceIndex << " ";
   m_os << "pkt-uid=" << packet.GetUid () << " ";
-  PrintType (packet);
+  packet.Print (m_os);
   m_os << std::endl;
 }
 void 
@@ -129,7 +131,7 @@
   context.Get (interfaceIndex);
   m_os << "interface=" << interfaceIndex << " ";
   m_os << "pkt-uid=" << p.GetUid () << " ";
-  PrintType (p);
+  p.Print (m_os);
   m_os << std::endl;  
 }
 
--- a/src/internet-node/ipv4-header.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/ipv4-header.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -28,17 +28,6 @@
 
 namespace ns3 {
 
-static uint16_t 
-UtilsNtoh16 (uint16_t v)
-{
-  uint16_t val;
-  uint8_t *array;
-  array = (uint8_t *)&v;
-  val = (array[0] << 8) | (array[1] << 0);
-  return val;
-}
-
-
 bool Ipv4Header::m_calcChecksum = false;
 
 Ipv4Header::Ipv4Header ()
@@ -190,21 +179,48 @@
   return m_goodChecksum;
 }
 
+std::string 
+Ipv4Header::DoGetName (void) const
+{
+  return "IPV4";
+}
+
 void 
 Ipv4Header::PrintTo (std::ostream &os) const
 {
   // ipv4, right ?
-  os << "(ipv4)"
-     << " tos=" << (uint32_t)m_tos
-     << ", payload length=" << UtilsNtoh16 (m_payloadSize)
-     << ", id=" << m_identification
-     << ", " << (IsLastFragment ()?"last":"more")
-     << ", " << (IsDontFragment ()?"dont":"may")
-     << ", frag offset=" << m_fragmentOffset
-     << ", ttl=" << m_ttl
-     << ", protocol=" << m_protocol
-     << ", source=" << m_source
-     << ", destination=" << m_destination;
+  std::string flags;
+  if (m_flags == 0)
+    {
+      flags = "none";
+    }
+  else if (m_flags & MORE_FRAGMENTS &&
+           m_flags & DONT_FRAGMENT)
+    {
+      flags = "MF|DF";
+    }
+  else if (m_flags & DONT_FRAGMENT)
+    {
+      flags = "DF";
+    }
+  else if (m_flags & MORE_FRAGMENTS)
+    {
+      flags = "MF";
+    }
+  else
+    {
+      flags = "XX";
+    }
+  os << "("
+     << "tos 0x" << std::hex << m_tos << std::dec << " "
+     << "ttl " << m_ttl << " "
+     << "id " << m_identification << " "
+     << "offset " << m_fragmentOffset << " "
+     << "flags [" << flags << "] "
+     << "length: " << (m_payloadSize + 5 * 4)
+     << ") "
+     << m_source << " > " << m_destination
+    ;
 }
 uint32_t 
 Ipv4Header::GetSerializedSize (void) const
--- a/src/internet-node/ipv4-header.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/ipv4-header.h	Fri Jul 06 14:39:59 2007 -0700
@@ -140,6 +140,7 @@
   bool IsChecksumOk (void) const;
 
 private:
+  virtual std::string DoGetName (void) const;
   virtual void PrintTo (std::ostream &os) const;
   virtual uint32_t GetSerializedSize (void) const;
   virtual void SerializeTo (Buffer::Iterator start) const;
--- a/src/internet-node/udp-header.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/udp-header.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -91,15 +91,20 @@
   m_initialChecksum = Ipv4ChecksumCalculate (0, buf, 12);
 }
 
-
+std::string 
+UdpHeader::DoGetName (void) const
+{
+  return "UDP";
+}
 
 void 
 UdpHeader::PrintTo (std::ostream &os) const
 {
-  os << "(udp)"
-     << ", port source=" << m_sourcePort
-     << ", port destination=" << m_destinationPort
-     << ", length=" << m_payloadSize;
+  os << "(" 
+     << "length: " << m_payloadSize + GetSize ()
+     << ") "
+     << m_sourcePort << " > " << m_destinationPort
+    ;
 }
 
 uint32_t 
--- a/src/internet-node/udp-header.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/internet-node/udp-header.h	Fri Jul 06 14:39:59 2007 -0700
@@ -81,6 +81,7 @@
                            uint8_t protocol);
 
 private:
+  virtual std::string DoGetName (void) const;
   virtual void PrintTo (std::ostream &os) const;
   virtual uint32_t GetSerializedSize (void) const;
   virtual void SerializeTo (Buffer::Iterator start) const;
--- a/src/node/llc-snap-header.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/node/llc-snap-header.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -47,14 +47,20 @@
   return 1 + 1 + 1 + 3 + 2;
 }
 
+std::string
+LlcSnapHeader::DoGetName (void) const
+{
+  return "LLCSNAP";
+}
+
 void 
 LlcSnapHeader::PrintTo (std::ostream &os) const
 {
-  os << "(mac)"
-      << " EtherType: ";
+  os << "(type 0x";
   os.setf (std::ios::hex, std::ios::basefield);
   os << m_etherType;
   os.setf (std::ios::dec, std::ios::basefield);
+  os << ")";
 }
 
 void
--- a/src/node/llc-snap-header.h	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/node/llc-snap-header.h	Fri Jul 06 14:39:59 2007 -0700
@@ -37,6 +37,7 @@
   uint16_t GetType (void);
 
 private:
+  virtual std::string DoGetName (void) const;
   virtual void PrintTo (std::ostream &os) const;
   virtual uint32_t GetSerializedSize (void) const;
   virtual void SerializeTo (Buffer::Iterator start) const;
--- a/src/simulator/time.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/simulator/time.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -191,7 +191,28 @@
 std::ostream& 
 operator<< (std::ostream& os, Time const& time)
 {
-  os << time.GetTimeStep () << "ts";
+  std::string unit;
+  switch (TimeStepPrecision::Get ()) {
+  case TimeStepPrecision::S:
+    unit = "s";
+    break;
+  case TimeStepPrecision::MS:
+    unit = "ms";
+    break;
+  case TimeStepPrecision::US:
+    unit = "us";
+    break;
+  case TimeStepPrecision::NS:
+    unit = "ns";
+    break;
+  case TimeStepPrecision::PS:
+    unit = "ps";
+    break;
+  case TimeStepPrecision::FS:
+    unit = "fs";
+    break;
+  }
+  os << time.GetTimeStep () << unit;
   return os;
 }
 
--- a/src/simulator/wscript	Fri Jul 06 14:19:40 2007 -0700
+++ b/src/simulator/wscript	Fri Jul 06 14:39:59 2007 -0700
@@ -7,7 +7,9 @@
 def set_options(opt):
     opt.add_option('--high-precision-as-double',
                    help=('Whether to use a double floating point'
-                         ' type for high precision time values'),
+                         ' type for high precision time values'
+                         ' WARNING: this option only has effect '
+                         'with the configure command.'),
                    action="store_true", default=False,
                    dest='high_precision_as_double')
 
--- a/utils/bench-packets.cc	Fri Jul 06 14:19:40 2007 -0700
+++ b/utils/bench-packets.cc	Fri Jul 06 14:39:59 2007 -0700
@@ -18,97 +18,168 @@
  *
  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
  */
-#include "ns3/wall-clock-ms.h"
+#include "ns3/system-wall-clock-ms.h"
 #include "ns3/packet.h"
-#include "ns3/chunk-constant-data.h"
-#include "ns3/chunk-udp.h"
-#include "ns3/chunk-ipv4.h"
+#include "ns3/packet-metadata.h"
 #include <iostream>
+#include <sstream>
 
 using namespace ns3;
 
+template <int N>
+class BenchHeader : public Header
+{
+public:
+  BenchHeader ();
+  bool IsOk (void) const;
+private:
+  virtual std::string DoGetName (void) const;
+  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);
+  bool m_ok;
+};
+
+template <int N>
+BenchHeader<N>::BenchHeader ()
+  : m_ok (false)
+{}
+
+template <int N>
+bool 
+BenchHeader<N>::IsOk (void) const
+{
+  return m_ok;
+}
+
+template <int N>
+std::string 
+BenchHeader<N>::DoGetName (void) const
+{
+  std::ostringstream oss;
+  oss << N;
+  return oss.str ();
+}
+
+template <int N>
+void 
+BenchHeader<N>::PrintTo (std::ostream &os) const
+{
+  NS_ASSERT (false);
+}
+template <int N>
+uint32_t 
+BenchHeader<N>::GetSerializedSize (void) const
+{
+  return N;
+}
+template <int N>
+void 
+BenchHeader<N>::SerializeTo (Buffer::Iterator start) const
+{
+  start.WriteU8 (N, N);
+}
+template <int N>
+uint32_t
+BenchHeader<N>::DeserializeFrom (Buffer::Iterator start)
+{
+  m_ok = true;
+  for (int i = 0; i < N; i++)
+    {
+      if (start.ReadU8 () != N)
+        {
+          m_ok = false;
+        }
+    }
+  return N;
+}
+
+
+
 static void 
 benchPtrA (uint32_t n)
 {
-  ChunkConstantData data = ChunkConstantData (2000, 1);
-  ChunkUdp udp;
-  ChunkIpv4 ipv4;
+  BenchHeader<25> ipv4;
+  BenchHeader<8> udp;
 
   for (uint32_t i = 0; i < n; i++) {
-      Packet p;
-      p.add (&data);
-      p.add (&udp);
-      p.add (&ipv4);
+      Packet p (2000);
+      p.AddHeader (udp);
+      p.AddHeader (ipv4);
       Packet o = p;
-      o.peek (&ipv4);
-      o.remove (&ipv4);
-      o.peek (&udp);
-      o.remove (&udp);
-      o.peek (&data);
-      o.remove (&data);
+      o.RemoveHeader (ipv4);
+      o.RemoveHeader (udp);
   }
 }
 
 static void 
 benchPtrB (uint32_t n)
 {
-  ChunkConstantData data = ChunkConstantData (2000, 1);
-  ChunkUdp udp;
-  ChunkIpv4 ipv4;
+  BenchHeader<25> ipv4;
+  BenchHeader<8> udp;
 
   for (uint32_t i = 0; i < n; i++) {
-      Packet p;
-      p.add (&data);
-      p.add (&udp);
-      p.add (&ipv4);
+      Packet p (2000);
+      p.AddHeader (udp);
+      p.AddHeader (ipv4);
   }
 }
 
 static void
 ptrC2 (Packet p)
 {
-  ChunkConstantData data = ChunkConstantData (2000, 1);
-  ChunkUdp udp;
+  BenchHeader<8> udp;
 
-  p.peek (&udp);
-  p.remove (&udp);
-  p.peek (&data);
-  p.remove (&data);
+  p.RemoveHeader (udp);
 }
 
 static void 
 ptrC1 (Packet p)
 {
-  ChunkIpv4 ipv4;
-  p.peek (&ipv4);
-  p.remove (&ipv4);
+  BenchHeader<25> ipv4;
+  p.RemoveHeader (ipv4);
   ptrC2 (p);
 }
 
 static void
 benchPtrC (uint32_t n)
 {
-  ChunkConstantData data = ChunkConstantData (2000, 1);
-  ChunkUdp udp;
-  ChunkIpv4 ipv4;
+  BenchHeader<25> ipv4;
+  BenchHeader<8> udp;
 
   for (uint32_t i = 0; i < n; i++) {
-      Packet p;
-      p.add (&data);
-      p.add (&udp);
-      p.add (&ipv4);
+      Packet p (2000);
+      p.AddHeader (udp);
+      p.AddHeader (ipv4);
       ptrC1 (p);
   }
 }
 
+static void
+benchPrint (uint32_t n)
+{
+  PacketPrinter printer;
+  BenchHeader<25> ipv4;
+  BenchHeader<8> udp;
+  Packet p (2000);
+  p.AddHeader (udp);
+  p.AddHeader (ipv4);
+
+  for (uint32_t i = 0; i < n; i++) 
+    {
+      p.Print (std::cerr, printer);
+    }  
+}
+
 
 static void
 runBench (void (*bench) (uint32_t), uint32_t n, char const *name)
 {
-  WallClockMs time;
-  time.start ();
+  SystemWallClockMs time;
+  time.Start ();
   (*bench) (n);
-  unsigned long long deltaMs = time.end ();
+  unsigned long long deltaMs = time.End ();
   double ps = n;
   ps *= 1000;
   ps /= deltaMs;
@@ -119,17 +190,31 @@
 {
   uint32_t n = 0;
   while (argc > 0) {
-      if (strncmp ("--n=", argv[0],strlen ("--n=")) == 0) {
+      if (strncmp ("--n=", argv[0],strlen ("--n=")) == 0) 
+        {
           char const *nAscii = argv[0] + strlen ("--n=");
           n = atoi (nAscii);
-      }
+        }
       argc--;
       argv++;
   }
 
+
   runBench (&benchPtrA, n, "a");
   runBench (&benchPtrB, n, "b");
   runBench (&benchPtrC, n, "c");
 
+  Packet::EnableMetadata ();
+  runBench (&benchPrint, n, "print");
+  PacketMetadata::SetOptOne (false);
+  runBench (&benchPtrA, n, "meta-a");
+  runBench (&benchPtrB, n, "meta-b");
+  runBench (&benchPtrC, n, "meta-c");
+  PacketMetadata::SetOptOne (true);
+  runBench (&benchPtrA, n, "meta-a-opt");
+  runBench (&benchPtrB, n, "meta-b-opt");
+  runBench (&benchPtrC, n, "meta-c-opt");
+
+
   return 0;
 }
--- a/wscript	Fri Jul 06 14:19:40 2007 -0700
+++ b/wscript	Fri Jul 06 14:39:59 2007 -0700
@@ -7,6 +7,7 @@
 import Params
 import Object
 import pproc as subprocess
+import optparse
 
 Params.g_autoconfig = 1
 
@@ -23,11 +24,32 @@
     shutil.rmtree("doc/latex")
 
 def set_options(opt):
+
+    def debug_option_callback(option, opt, value, parser):
+        if value == 'debug':
+            setattr(parser.values, option.dest, 'ultradebug')
+        elif value == 'optimized':
+            setattr(parser.values, option.dest, 'optimized')
+        else:
+            raise optparse.OptionValueError("allowed --debug-level values"
+                                            " are debug, optimized.")
+
+    opt.add_option('-d', '--debug-level',
+                   action='callback',
+                   type=str, dest='debug_level', default='debug',
+                   help=('Specify the debug level, does nothing if CFLAGS is set'
+                         ' in the environment. [Allowed Values: debug, optimized].'
+                         ' WARNING: this option only has effect '
+                         'with the configure command.'),
+                   callback=debug_option_callback)
+    
     # options provided by the modules
     opt.tool_options('compiler_cxx')
 
     opt.add_option('--enable-gcov',
-                   help=('Enable code coverage analysis'),
+                   help=('Enable code coverage analysis.'
+                         ' WARNING: this option only has effect '
+                         'with the configure command.'),
                    action="store_true", default=False,
                    dest='enable_gcov')
 
@@ -59,10 +81,13 @@
     if not conf.check_tool('compiler_cxx'):
         Params.fatal("No suitable compiler found")
 
-
     # create the second environment, set the variant and set its name
     variant_env = conf.env.copy()
-    variant_name = Params.g_options.debug_level.lower()
+    debug_level = Params.g_options.debug_level.lower()
+    if debug_level == 'ultradebug':
+        variant_name = 'debug'
+    else:
+        variant_name = debug_level
 
     if Params.g_options.enable_gcov:
         variant_name += '-gcov'
@@ -79,13 +104,17 @@
     conf.setenv(variant_name)
 
     variant_env.append_value('CXXDEFINES', 'RUN_SELF_TESTS')
-    variant_env.append_value('CXXFLAGS', ['-Wall', '-Werror'])
+    
+    if os.path.basename(conf.env['CXX']).startswith("g++"):
+        variant_env.append_value('CXXFLAGS', ['-Wall', '-Werror'])
+
     if 'debug' in Params.g_options.debug_level.lower():
         variant_env.append_value('CXXDEFINES', 'NS3_DEBUG_ENABLE')
         variant_env.append_value('CXXDEFINES', 'NS3_ASSERT_ENABLE')
 
     if sys.platform == 'win32':
-        variant_env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc")
+        if os.path.basename(conf.env['CXX']).startswith("g++"):
+            variant_env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc")
 
     conf.sub_config('src')
 
@@ -95,10 +124,7 @@
     variant_env = bld.env_of_name(variant_name)
     bld.m_allenvs['default'] = variant_env # switch to the active variant
 
-    if Params.g_options.run:
-        run_program(Params.g_options.run)
-        return
-    elif Params.g_options.shell:
+    if Params.g_options.shell:
         run_shell()
         return
 
@@ -125,11 +151,19 @@
     if Params.g_options.doxygen:
         doxygen()
 
+    if Params.g_options.run:
+        run_program(Params.g_options.run)
+
 def _find_program(program_name):
+    found_programs = []
     for obj in Object.g_allobjs:
+        if obj.m_type != 'program' or not obj.target:
+            continue
+        found_programs.append(obj.target)
         if obj.target == program_name:
             return obj
-    raise ValueError("progam '%s' not found" % (program_name,))
+    raise ValueError("program '%s' not found; available programs are: %r"
+                     % (program_name, found_programs))
 
 def _run_argv(argv):
     env = Params.g_build.env_of_name('default')
@@ -142,6 +176,9 @@
     elif sys.platform == 'win32':
         pathvar = 'PATH'
         pathsep = ';'
+    elif sys.platform == 'cygwin':
+        pathvar = 'PATH'
+        pathsep = ':'
     else:
         Params.warning(("Don't know how to configure "
                         "dynamic library path for the platform '%s'") % (sys.platform,))
@@ -167,8 +204,8 @@
 
     try:
         program_obj = _find_program(program_name)
-    except ValueError:
-        Params.fatal("progam '%s' not found" % (program_name,))
+    except ValueError, ex:
+        Params.fatal(str(ex))
 
     try:
         program_node, = program_obj.m_linktask.m_outputs