a new tag implementation: Mtag
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Thu, 24 Apr 2008 14:52:59 -0700
changeset 3035 644bfc099992
parent 3034 b96c58f42432
child 3036 8e29fb88ad26
a new tag implementation: Mtag
src/common/buffer.cc
src/common/buffer.h
src/common/mtag-buffer.cc
src/common/mtag-buffer.h
src/common/mtag-list.cc
src/common/mtag-list.h
src/common/mtag.cc
src/common/mtag.h
src/common/packet.cc
src/common/packet.h
src/common/wscript
--- a/src/common/buffer.cc	Wed Apr 23 15:01:27 2008 -0700
+++ b/src/common/buffer.cc	Thu Apr 24 14:52:59 2008 -0700
@@ -333,10 +333,10 @@
   return m_end - (m_zeroAreaEnd - m_zeroAreaStart);
 }
 
-int32_t
+bool
 Buffer::AddAtStart (uint32_t start)
 {
-  int32_t delta;
+  bool dirty;
   NS_ASSERT (CheckInternalState ());
   bool isDirty = m_data->m_count > 1 && m_start > m_data->m_dirtyStart;
   if (m_start >= start && !isDirty)
@@ -348,7 +348,7 @@
        */
       NS_ASSERT (m_data->m_count == 1 || m_start == m_data->m_dirtyStart);
       m_start -= start;
-      delta = 0;
+      dirty = m_start > m_data->m_dirtyStart;
       HEURISTICS (g_nAddNoRealloc++);
     } 
   else
@@ -363,13 +363,14 @@
         }
       m_data = newData;
 
-      delta = start - m_start;
+      int32_t delta = start - m_start;
       m_start += delta;
       m_zeroAreaStart += delta;
       m_zeroAreaEnd += delta;
       m_end += delta;
+      m_start -= start;
 
-      m_start -= start;
+      dirty = true;
 
       HEURISTICS (g_nAddRealloc++);
     }
@@ -379,12 +380,12 @@
   m_data->m_dirtyEnd = m_end;
   LOG_INTERNAL_STATE ("add start=" << start << ", ");
   NS_ASSERT (CheckInternalState ());
-  return delta;
+  return dirty;
 }
-int32_t
+bool
 Buffer::AddAtEnd (uint32_t end)
 {
-  int32_t delta;
+  bool dirty;
   NS_ASSERT (CheckInternalState ());
   bool isDirty = m_data->m_count > 1 && m_end < m_data->m_dirtyEnd;
   if (GetInternalEnd () + end <= m_data->m_size && !isDirty)
@@ -397,7 +398,7 @@
       NS_ASSERT (m_data->m_count == 1 || m_end == m_data->m_dirtyEnd);
       m_end += end;
 
-      delta = 0;
+      dirty = m_end < m_data->m_dirtyEnd;
 
       HEURISTICS (g_nAddNoRealloc++);
     } 
@@ -413,14 +414,14 @@
         }
       m_data = newData;
 
-      delta = -m_start;
-
+      int32_t delta = -m_start;
       m_zeroAreaStart += delta;
       m_zeroAreaEnd += delta;
       m_end += delta;
       m_start += delta;
+      m_end += end;
 
-      m_end += end;
+      dirty = true;
 
       HEURISTICS (g_nAddRealloc++);
     } 
@@ -431,13 +432,12 @@
   LOG_INTERNAL_STATE ("add end=" << end << ", ");
   NS_ASSERT (CheckInternalState ());
 
-  return delta;
+  return dirty;
 }
 
-int32_t
+void
 Buffer::AddAtEnd (const Buffer &o)
 {
-  int32_t orgStart = m_start;
   if (m_end == m_zeroAreaEnd &&
       o.m_start == o.m_zeroAreaStart &&
       o.m_zeroAreaEnd - o.m_zeroAreaStart > 0)
@@ -457,7 +457,7 @@
       Buffer::Iterator src = o.End ();
       src.Prev (endData);
       dst.Write (src, o.End ());
-      return m_start - orgStart;
+      return;
     }
   Buffer dst = CreateFullCopy ();
   Buffer src = o.CreateFullCopy ();
@@ -467,7 +467,6 @@
   destStart.Prev (src.GetSize ());
   destStart.Write (src.Begin (), src.End ());
   *this = dst;
-  return m_start - orgStart;
 }
 
 void 
@@ -583,6 +582,18 @@
   return *this;
 }
 
+int32_t 
+Buffer::GetCurrentStartOffset (void) const
+{
+  return m_start;
+}
+int32_t 
+Buffer::GetCurrentEndOffset (void) const
+{
+  return m_end;
+}
+
+
 void
 Buffer::TransformIntoRealBuffer (void) const
 {
--- a/src/common/buffer.h	Wed Apr 23 15:01:27 2008 -0700
+++ b/src/common/buffer.h	Thu Apr 24 14:52:59 2008 -0700
@@ -392,51 +392,35 @@
 
   /**
    * \param start size to reserve
-   * \returns the adjustment delta.
+   * \returns true if the buffer needed resizing, false otherwise.
    *
    * Add bytes at the start of the Buffer. The
    * content of these bytes is undefined but debugging
    * builds initialize them to 0x33.
    * Any call to this method invalidates any Iterator
    * pointing to this Buffer.
-   *
-   * The value returned by this method indicates how the internal
-   * buffer was modified to handle the user request to reserve space.
-   * If that value is zero, the buffer was not modified: the user
-   * request was completed without further memory allocation.
    */
-  int32_t AddAtStart (uint32_t start);
+  bool AddAtStart (uint32_t start);
   /**
    * \param end size to reserve
-   * \returns the adjustment delta.
+   * \returns true if the buffer needed resizing, false otherwise.
    *
    * Add bytes at the end of the Buffer. The
    * content of these bytes is undefined but debugging
    * builds initialize them to 0x33.
    * Any call to this method invalidates any Iterator
    * pointing to this Buffer.
-   *
-   * The value returned by this method indicates how the internal
-   * buffer was modified to handle the user request to reserve space.
-   * If that value is zero, the buffer was not modified: the user
-   * request was completed without further memory allocation.
    */
-  int32_t AddAtEnd (uint32_t end);
+  bool AddAtEnd (uint32_t end);
 
   /**
    * \param o the buffer to append to the end of this buffer.
-   * \returns the adjustment delta.
    *
    * Add bytes at the end of the Buffer.
    * Any call to this method invalidates any Iterator
    * pointing to this Buffer.
-   *
-   * The value returned by this method indicates how the internal
-   * buffer was modified to handle the user request to reserve space.
-   * If that value is zero, the buffer was not modified: the user
-   * request was completed without further memory allocation.
    */
-  int32_t AddAtEnd (const Buffer &o);
+  void AddAtEnd (const Buffer &o);
   /**
    * \param start size to remove
    *
@@ -476,6 +460,9 @@
 
   Buffer CreateFullCopy (void) const;
 
+  int32_t GetCurrentStartOffset (void) const;
+  int32_t GetCurrentEndOffset (void) const;
+
   Buffer (Buffer const &o);
   Buffer &operator = (Buffer const &o);
   Buffer ();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/mtag-buffer.cc	Thu Apr 24 14:52:59 2008 -0700
@@ -0,0 +1,124 @@
+#include "mtag-buffer.h"
+#include "ns3/assert.h"
+
+namespace ns3 {
+
+void 
+MtagBuffer::WriteU8 (uint8_t v)
+{
+  NS_ASSERT (m_current + 1 <= m_end);
+  *m_current = v;
+  m_current++;
+}
+void 
+MtagBuffer::WriteU16 (uint16_t data)
+{
+  WriteU8 ((data >> 0) & 0xff);
+  WriteU8 ((data >> 8) & 0xff);
+}
+void 
+MtagBuffer::WriteU32 (uint32_t data)
+{
+  WriteU8 ((data >> 0) & 0xff);
+  WriteU8 ((data >> 8) & 0xff);
+  WriteU8 ((data >> 16) & 0xff);
+  WriteU8 ((data >> 24) & 0xff);
+}
+void 
+MtagBuffer::WriteU64 (uint64_t data)
+{
+  WriteU8 ((data >> 0) & 0xff);
+  WriteU8 ((data >> 8) & 0xff);
+  WriteU8 ((data >> 16) & 0xff);
+  WriteU8 ((data >> 24) & 0xff);
+  WriteU8 ((data >> 32) & 0xff);
+  WriteU8 ((data >> 40) & 0xff);
+  WriteU8 ((data >> 48) & 0xff);
+  WriteU8 ((data >> 54) & 0xff);
+}
+uint8_t  
+MtagBuffer::ReadU8 (void)
+{
+  NS_ASSERT (m_current + 1 <= m_end);
+  uint8_t v;
+  v = *m_current;
+  m_current++;
+  return v;
+}
+uint16_t 
+MtagBuffer::ReadU16 (void)
+{
+  uint8_t byte0 = ReadU8 ();
+  uint8_t byte1 = ReadU8 ();
+  uint16_t data = byte1;
+  data <<= 8;
+  data |= byte0;
+  return data;
+}
+uint32_t 
+MtagBuffer::ReadU32 (void)
+{
+  uint8_t byte0 = ReadU8 ();
+  uint8_t byte1 = ReadU8 ();
+  uint8_t byte2 = ReadU8 ();
+  uint8_t byte3 = ReadU8 ();
+  uint32_t data = byte3;
+  data <<= 8;
+  data |= byte2;
+  data <<= 8;
+  data |= byte1;
+  data <<= 8;
+  data |= byte0;
+  return data;
+}
+uint64_t 
+MtagBuffer::ReadU64 (void)
+{
+  uint8_t byte0 = ReadU8 ();
+  uint8_t byte1 = ReadU8 ();
+  uint8_t byte2 = ReadU8 ();
+  uint8_t byte3 = ReadU8 ();
+  uint8_t byte4 = ReadU8 ();
+  uint8_t byte5 = ReadU8 ();
+  uint8_t byte6 = ReadU8 ();
+  uint8_t byte7 = ReadU8 ();
+  uint32_t data = byte7;
+  data <<= 8;
+  data |= byte6;
+  data <<= 8;
+  data |= byte5;
+  data <<= 8;
+  data |= byte4;
+  data <<= 8;
+  data |= byte3;
+  data <<= 8;
+  data |= byte2;
+  data <<= 8;
+  data |= byte1;
+  data <<= 8;
+  data |= byte0;
+
+  return data;
+}
+MtagBuffer::MtagBuffer (uint8_t *start, uint8_t *end)
+  : m_current (start),
+    m_end (end)
+{}
+
+void 
+MtagBuffer::TrimAtEnd (uint32_t trim)
+{
+  NS_ASSERT (m_current <= (m_end - trim));
+  m_end -= trim;
+}
+
+void 
+MtagBuffer::CopyFrom (MtagBuffer o)
+{
+  uint32_t size = o.m_end - o.m_current;
+  NS_ASSERT (size <= m_end - m_current);
+  memcpy (m_current, o.m_current, size);
+  m_current += size;
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/mtag-buffer.h	Thu Apr 24 14:52:59 2008 -0700
@@ -0,0 +1,32 @@
+#ifndef MTAG_BUFFER_H
+#define MTAG_BUFFER_H
+
+#include <stdint.h>
+
+namespace ns3 {
+
+class MtagBuffer
+{
+public:
+  MtagBuffer (uint8_t *start, uint8_t *end);
+  void TrimAtEnd (uint32_t trim);
+
+  void WriteU8 (uint8_t v);
+  void WriteU16 (uint16_t v);
+  void WriteU32 (uint32_t v);
+  void WriteU64 (uint64_t v);
+  uint8_t  ReadU8 (void);
+  uint16_t ReadU16 (void);
+  uint32_t ReadU32 (void);
+  uint64_t ReadU64 (void);
+
+  void CopyFrom (MtagBuffer o);
+private:
+  
+  uint8_t *m_current;
+  uint8_t *m_end;
+};
+
+} // namespace ns3
+
+#endif /* MTAG_BUFFER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/mtag-list.cc	Thu Apr 24 14:52:59 2008 -0700
@@ -0,0 +1,237 @@
+#include "mtag-list.h"
+
+namespace ns3 {
+
+MtagList::Iterator::Item::Item (MtagBuffer buf_)
+    : buf (buf_)
+{}
+
+bool 
+MtagList::Iterator::HasNext (void) const
+{
+  return m_current < m_end;
+}
+struct MtagList::Iterator::Item 
+MtagList::Iterator::Next (void)
+{
+  NS_ASSERT (HasNext ());
+  struct Item item = Item (MtagBuffer (m_current, m_end));
+  item.tid.SetUid (item.buf.ReadU32 ());
+  item.size = item.buf.ReadU32 ();
+  item.start = item.buf.ReadU32 ();
+  item.end = item.buf.ReadU32 ();
+  item.start = std::max (item.start, m_offsetStart);
+  item.end = std::min (item.end, m_offsetEnd);
+  m_current += 4 + 4 + 4 + 4 + item.size;
+  item.buf.TrimAtEnd (m_end - m_current);
+  PrepareForNext ();
+  return item;
+}
+void
+MtagList::Iterator::PrepareForNext (void)
+{
+  while (m_current < m_end)
+    {
+      struct Item item = Item (MtagBuffer (m_current, m_end));
+      item.tid.SetUid (item.buf.ReadU32 ());
+      item.size = item.buf.ReadU32 ();
+      item.start = item.buf.ReadU32 ();
+      item.end = item.buf.ReadU32 ();
+      if (item.start > m_offsetEnd || item.end < m_offsetStart)
+	{
+	  m_current += 4 + 4 + 4 + 4 + item.size;
+	}
+      else
+	{
+	  break;
+	}
+    }
+}
+MtagList::Iterator::Iterator (uint8_t *start, uint8_t *end, uint32_t offsetStart, uint32_t offsetEnd)
+  : m_current (start),
+    m_end (end),
+    m_offsetStart (offsetStart),
+    m_offsetEnd (offsetEnd)
+{
+  PrepareForNext ();
+}
+
+uint32_t 
+MtagList::Iterator::GetOffsetStart (void) const
+{
+  return m_offsetStart;
+}
+
+
+MtagList::MtagList ()
+  : m_buffer (0),
+    m_size (0)
+{}
+MtagList::MtagList (const MtagList &o)
+  : m_size (o.m_size)
+{
+  m_buffer = new uint8_t [o.m_size] ();
+  memcpy (m_buffer, o.m_buffer, o.m_size);
+}
+MtagList &
+MtagList::operator = (const MtagList &o)
+{
+  delete [] m_buffer;
+  m_buffer = new uint8_t [o.m_size] ();
+  memcpy (m_buffer, o.m_buffer, m_size);
+  m_size = o.m_size;
+  return *this;
+}
+MtagList::~MtagList ()
+{
+  delete [] m_buffer;
+  m_buffer = 0;
+  m_size = 0;
+}
+
+MtagBuffer
+MtagList::Add (TypeId tid, uint32_t bufferSize, uint32_t start, uint32_t end)
+{
+  uint32_t newSize = m_size + bufferSize + 4 + 4 + 4 + 4;
+  uint8_t *newBuffer = new uint8_t [newSize] ();
+  memcpy (newBuffer, m_buffer, m_size);
+  MtagBuffer tag = MtagBuffer (newBuffer + m_size, newBuffer + newSize);
+  tag.WriteU32 (tid.GetUid ());
+  tag.WriteU32 (bufferSize);
+  tag.WriteU32 (start);
+  tag.WriteU32 (end);
+  delete [] m_buffer;
+  m_buffer = newBuffer;
+  m_size = newSize;
+  return tag;
+}
+
+void 
+MtagList::Add (const MtagList &o)
+{
+  MtagList::Iterator i = o.Begin (0, 0xffffffff);
+  while (i.HasNext ())
+    {
+      MtagList::Iterator::Item item = i.Next ();
+      MtagBuffer buf = Add (item.tid, item.size, item.start, item.end);
+      buf.CopyFrom (item.buf);
+    }
+}
+
+void 
+MtagList::Remove (const Iterator &i)
+{
+  // XXX: more complex to implement.
+}
+
+void 
+MtagList::RemoveAll (void)
+{
+  delete [] m_buffer;
+  m_buffer = 0;
+  m_size = 0;  
+}
+
+MtagList::Iterator 
+MtagList::Begin (uint32_t offsetStart, uint32_t offsetEnd) const
+{
+  return Iterator (m_buffer, m_buffer + m_size, offsetStart, offsetEnd);
+}
+
+bool 
+MtagList::IsDirtyAtEnd (uint32_t appendOffset)
+{
+  MtagList::Iterator i = Begin (0, 0xffffffff);
+  while (i.HasNext ())
+    {
+      MtagList::Iterator::Item item = i.Next ();
+      if (item.end > appendOffset)
+	{
+	  return true;
+	}
+    }
+  return false;
+}
+
+bool 
+MtagList::IsDirtyAtStart (uint32_t prependOffset)
+{
+  MtagList::Iterator i = Begin (0, 0xffffffff);
+  while (i.HasNext ())
+    {
+      MtagList::Iterator::Item item = i.Next ();
+      if (item.start < prependOffset)
+	{
+	  return true;
+	}
+    }
+  return false;
+}
+
+void 
+MtagList::AddAtEnd (int32_t adjustment, uint32_t appendOffset)
+{
+  if (adjustment == 0 && !IsDirtyAtEnd (appendOffset))
+    {
+      return;
+    }
+  MtagList list;
+  MtagList::Iterator i = Begin (0, 0xffffffff);
+  while (i.HasNext ())
+    {
+      MtagList::Iterator::Item item = i.Next ();
+      item.start += adjustment;
+      item.end += adjustment;
+
+      if (item.start > appendOffset)
+	{
+	  continue;
+	}
+      else if (item.start < appendOffset && item.end > appendOffset)
+	{
+	  item.end = appendOffset;
+	}
+      else
+	{
+	  // nothing to do.
+	}
+      MtagBuffer buf = list.Add (item.tid, item.size, item.start, item.end);
+      buf.CopyFrom (item.buf);
+    }
+  *this = list;  
+}
+
+void 
+MtagList::AddAtStart (int32_t adjustment, uint32_t prependOffset)
+{
+  if (adjustment == 0 && !IsDirtyAtStart (prependOffset))
+    {
+      return;
+    }
+  MtagList list;
+  MtagList::Iterator i = Begin (0, 0xffffffff);
+  while (i.HasNext ())
+    {
+      MtagList::Iterator::Item item = i.Next ();
+      item.start += adjustment;
+      item.end += adjustment;
+
+      if (item.end < prependOffset)
+	{
+	  continue;
+	}
+      else if (item.end > prependOffset && item.start < prependOffset)
+	{
+	  item.start = prependOffset;
+	}
+      else
+	{
+	  // nothing to do.
+	}
+      MtagBuffer buf = list.Add (item.tid, item.size, item.start, item.end);
+      buf.CopyFrom (item.buf);
+    }
+  *this = list;    
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/mtag-list.h	Thu Apr 24 14:52:59 2008 -0700
@@ -0,0 +1,67 @@
+#ifndef MTAG_LIST_H
+#define MTAG_LIST_H
+
+#include <stdint.h>
+#include "ns3/type-id.h"
+#include "mtag-buffer.h"
+
+namespace ns3 {
+
+class MtagList
+{
+public:
+
+  class Iterator
+  {
+  public:
+    struct Item 
+    {
+      TypeId tid;
+      uint32_t size;
+      uint32_t start;
+      uint32_t end;
+      MtagBuffer buf;
+    private:
+      friend class MtagList;
+      Item (MtagBuffer buf);
+    };
+    bool HasNext (void) const;
+    struct MtagList::Iterator::Item Next (void);
+    uint32_t GetOffsetStart (void) const;
+  private:
+    friend class MtagList;
+    Iterator (uint8_t *start, uint8_t *end, uint32_t offsetStart, uint32_t offsetEnd);
+    void PrepareForNext (void);
+    uint8_t *m_current;
+    uint8_t *m_end;
+    uint32_t m_offsetStart;
+    uint32_t m_offsetEnd;
+  };
+
+  MtagList ();
+  MtagList (const MtagList &o);
+  MtagList &operator = (const MtagList &o);
+  ~MtagList ();
+
+  MtagBuffer Add (TypeId tid, uint32_t bufferSize, uint32_t start, uint32_t end);
+
+  void Add (const MtagList &o);
+
+  void Remove (const Iterator &i);
+  void RemoveAll (void);
+
+  MtagList::Iterator Begin (uint32_t offsetStart, uint32_t offsetEnd) const;
+
+  void AddAtEnd (int32_t adjustment, uint32_t appendOffset);
+  void AddAtStart (int32_t adjustment, uint32_t prependOffset);
+
+private:
+  bool IsDirtyAtEnd (uint32_t appendOffset);
+  bool IsDirtyAtStart (uint32_t prependOffset);
+  uint8_t *m_buffer;
+  uint32_t m_size;
+};
+
+} // namespace ns3
+
+#endif /* MTAG_LIST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/mtag.cc	Thu Apr 24 14:52:59 2008 -0700
@@ -0,0 +1,15 @@
+#include "mtag.h"
+
+namespace ns3 {
+
+TypeId 
+Mtag::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::Mtag")
+    .SetParent<ObjectBase> ()
+    ;
+  return tid;
+}
+
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/mtag.h	Thu Apr 24 14:52:59 2008 -0700
@@ -0,0 +1,22 @@
+#ifndef MTAG_H
+#define MTAG_H
+
+#include "ns3/object-base.h"
+#include "mtag-buffer.h"
+#include <stdint.h>
+
+namespace ns3 {
+
+class Mtag : public ObjectBase
+{
+public:
+  static TypeId GetTypeId (void);
+
+  virtual uint32_t GetSerializedSize (void) const = 0;
+  virtual void Serialize (MtagBuffer i) const = 0;
+  virtual void Deserialize (MtagBuffer i) = 0;
+};
+
+} // namespace ns3
+
+#endif /* MTAG_H */
--- a/src/common/packet.cc	Wed Apr 23 15:01:27 2008 -0700
+++ b/src/common/packet.cc	Thu Apr 24 14:52:59 2008 -0700
@@ -24,6 +24,59 @@
 
 uint32_t Packet::m_globalUid = 0;
 
+TypeId 
+TagIterator::Item::GetTypeId (void) const
+{
+  return m_tid;
+}
+uint32_t 
+TagIterator::Item::GetStart (void) const
+{
+  return m_start;
+}
+uint32_t 
+TagIterator::Item::GetEnd (void) const
+{
+  return m_end;
+}
+void 
+TagIterator::Item::GetTag (Mtag &tag) const
+{
+  if (tag.GetInstanceTypeId () != GetTypeId ())
+    {
+      NS_FATAL_ERROR ("The tag you provided is not of the right type.");
+    }
+  tag.Deserialize (m_buffer);
+}
+TagIterator::Item::Item (TypeId tid, uint32_t start, uint32_t end, MtagBuffer buffer)
+  : m_tid (tid),
+    m_start (start),
+    m_end (end),
+    m_buffer (buffer)
+{}
+bool 
+TagIterator::HasNext (void) const
+{
+  return m_current.HasNext ();
+}
+TagIterator::Item 
+TagIterator::Next (void)
+{
+  MtagList::Iterator::Item i = m_current.Next ();
+  
+  TagIterator::Item item = TagIterator::Item (i.tid, 
+                                              i.start-m_current.GetOffsetStart (), 
+                                              i.end-m_current.GetOffsetStart (), 
+                                              i.buf);
+  
+  
+  return item;
+}
+TagIterator::TagIterator (MtagList::Iterator i)
+  : m_current (i)
+{}
+
+
 void 
 Packet::Ref (void) const
 {
@@ -51,6 +104,7 @@
 Packet::Packet ()
   : m_buffer (),
     m_tags (),
+    m_tagList (),
     m_metadata (m_globalUid, 0),
     m_refCount (1)
 {
@@ -60,6 +114,7 @@
 Packet::Packet (const Packet &o)
   : m_buffer (o.m_buffer),
     m_tags (o.m_tags),
+    m_tagList (o.m_tagList),
     m_metadata (o.m_metadata),
     m_refCount (1)
 {}
@@ -73,6 +128,7 @@
     }
   m_buffer = o.m_buffer;
   m_tags = o.m_tags;
+  m_tagList = o.m_tagList;
   m_metadata = o.m_metadata;
   return *this;
 }
@@ -80,6 +136,7 @@
 Packet::Packet (uint32_t size)
   : m_buffer (size),
     m_tags (),
+    m_tagList (),
     m_metadata (m_globalUid, size),
     m_refCount (1)
 {
@@ -88,6 +145,7 @@
 Packet::Packet (uint8_t const*buffer, uint32_t size)
   : m_buffer (),
     m_tags (),
+    m_tagList (),
     m_metadata (m_globalUid, size),
     m_refCount (1)
 {
@@ -97,9 +155,10 @@
   i.Write (buffer, size);
 }
 
-Packet::Packet (Buffer buffer, Tags tags, PacketMetadata metadata)
+Packet::Packet (const Buffer &buffer, const Tags &tags, const MtagList &tagList, const PacketMetadata &metadata)
   : m_buffer (buffer),
     m_tags (tags),
+    m_tagList (tagList),
     m_metadata (metadata),
     m_refCount (1)
 {}
@@ -113,7 +172,7 @@
   PacketMetadata metadata = m_metadata.CreateFragment (start, end);
   // again, call the constructor directly rather than
   // through Create because it is private.
-  return Ptr<Packet> (new Packet (buffer, m_tags, metadata), false);
+  return Ptr<Packet> (new Packet (buffer, m_tags, m_tagList, metadata), false);
 }
 
 uint32_t 
@@ -126,7 +185,13 @@
 Packet::AddHeader (const Header &header)
 {
   uint32_t size = header.GetSerializedSize ();
-  m_buffer.AddAtStart (size);
+  uint32_t orgStart = m_buffer.GetCurrentStartOffset ();
+  bool resized = m_buffer.AddAtStart (size);
+  if (resized)
+    {
+      m_tagList.AddAtStart (m_buffer.GetCurrentStartOffset () - orgStart,
+                            m_buffer.GetCurrentStartOffset () + size);
+    }
   header.Serialize (m_buffer.Begin ());
   m_metadata.AddHeader (header, size);
 }
@@ -142,7 +207,13 @@
 Packet::AddTrailer (const Trailer &trailer)
 {
   uint32_t size = trailer.GetSerializedSize ();
-  m_buffer.AddAtEnd (size);
+  uint32_t orgEnd = m_buffer.GetCurrentEndOffset ();
+  bool resized = m_buffer.AddAtEnd (size);
+  if (resized)
+    {
+      m_tagList.AddAtEnd (m_buffer.GetCurrentEndOffset () - orgEnd,
+                          m_buffer.GetCurrentEndOffset () - size);
+    }
   Buffer::Iterator end = m_buffer.End ();
   trailer.Serialize (end);
   m_metadata.AddTrailer (trailer, size);
@@ -159,17 +230,28 @@
 void 
 Packet::AddAtEnd (Ptr<const Packet> packet)
 {
+  uint32_t aStart = m_buffer.GetCurrentStartOffset ();
+  uint32_t bEnd = packet->m_buffer.GetCurrentEndOffset ();
   m_buffer.AddAtEnd (packet->m_buffer);
-  /**
-   * XXX: we might need to merge the tag list of the
-   * other packet into the current packet.
-   */
+  uint32_t appendPrependOffset = m_buffer.GetCurrentEndOffset () - packet->m_buffer.GetSize ();
+  m_tagList.AddAtEnd (m_buffer.GetCurrentStartOffset () - aStart, 
+                      appendPrependOffset);
+  MtagList copy = packet->m_tagList;
+  copy.AddAtStart (m_buffer.GetCurrentEndOffset () - bEnd,
+                   appendPrependOffset);
+  m_tagList.Add (copy);
   m_metadata.AddAtEnd (packet->m_metadata);
 }
 void
 Packet::AddPaddingAtEnd (uint32_t size)
 {
-  m_buffer.AddAtEnd (size);
+  uint32_t orgEnd = m_buffer.GetCurrentEndOffset ();
+  bool resized = m_buffer.AddAtEnd (size);
+  if (resized)
+    {
+      m_tagList.AddAtEnd (m_buffer.GetCurrentEndOffset () - orgEnd,
+                          m_buffer.GetCurrentEndOffset () - size);
+    }
   m_metadata.AddPaddingAtEnd (size);
 }
 void 
@@ -189,6 +271,7 @@
 Packet::RemoveAllTags (void)
 {
   m_tags.RemoveAll ();
+  m_tagList.RemoveAll ();
 }
 
 uint8_t const *
@@ -207,6 +290,7 @@
 Packet::PrintTags (std::ostream &os) const
 {
   m_tags.Print (os, " ");
+  // XXX: tagList.
 }
 
 void 
@@ -388,6 +472,38 @@
   buffer.RemoveAtStart (metadataDeserialized);
 }
 
+void 
+Packet::AddMtag (const Mtag &tag) const
+{
+  MtagList *list = const_cast<MtagList *> (&m_tagList);
+  MtagBuffer buffer = list->Add (tag.GetInstanceTypeId (), tag.GetSerializedSize (), 
+                                 m_buffer.GetCurrentStartOffset (),
+                                 m_buffer.GetCurrentEndOffset ());
+  tag.Serialize (buffer);
+}
+TagIterator 
+Packet::GetTagIterator (void) const
+{
+  return TagIterator (m_tagList.Begin (m_buffer.GetCurrentStartOffset (), m_buffer.GetCurrentEndOffset ()));
+}
+
+bool 
+Packet::FindFirstMatchingTag (Mtag &tag) const
+{
+  TypeId tid = tag.GetInstanceTypeId ();
+  TagIterator i = GetTagIterator ();
+  while (i.HasNext ())
+    {
+      TagIterator::Item item = i.Next ();
+      if (tid == item.GetTypeId ())
+        {
+          item.GetTag (tag);
+          return true;
+        }
+    }
+  return false;
+}
+
 std::ostream& operator<< (std::ostream& os, const Packet &packet)
 {
   packet.Print (os);
@@ -403,24 +519,136 @@
 
 #include "ns3/test.h"
 #include <string>
+#include <stdarg.h>
+
+using namespace ns3;
+
+namespace {
+
+class ATestTagBase : public Mtag
+{
+public:
+  ATestTagBase () : m_error (false) {}
+  bool m_error;
+};
+
+template <int N>
+class ATestTag : public ATestTagBase
+{
+public:
+  static TypeId GetTypeId (void) {
+    std::ostringstream oss;
+    oss << "anon::ATestTag<" << N << ">";
+    static TypeId tid = TypeId (oss.str ().c_str ())
+      .SetParent<Mtag> ()
+      .AddConstructor<ATestTag<N> > ()
+      .HideFromDocumentation ()
+      ;
+    return tid;
+  }
+  virtual TypeId GetInstanceTypeId (void) const {
+    return GetTypeId ();
+  }
+  virtual uint32_t GetSerializedSize (void) const {
+    return N;
+  }
+  virtual void Serialize (MtagBuffer buf) const {
+    for (uint32_t i = 0; i < N; ++i)
+      {
+        buf.WriteU8 (N);
+      }
+  }
+  virtual void Deserialize (MtagBuffer buf) {
+    for (uint32_t i = 0; i < N; ++i)
+      {
+        uint8_t v = buf.ReadU8 ();
+        if (v != N)
+          {
+            m_error = true;
+          }
+      }
+  }
+  ATestTag ()
+    : ATestTagBase () {}
+};
+
+struct Expected
+{
+  Expected (uint32_t n_, uint32_t start_, uint32_t end_)
+    : n (n_), start (start_), end (end_) {}
+  
+  uint32_t n;
+  uint32_t start;
+  uint32_t end;
+};
+
+}
+
+#define E(a,b,c) a,b,c
+
+#define CHECK(p, n, ...)                                \
+  if (!DoCheck (p, __FILE__, __LINE__, n, __VA_ARGS__)) \
+    {                                                   \
+      result = false;                                   \
+    }
 
 namespace ns3 {
 
-class PacketTest: public Test {
+
+class PacketTest: public Test 
+{
 public:
+  PacketTest ();
   virtual bool RunTests (void);
-  PacketTest ();
+private:
+  bool DoCheck (Ptr<const Packet> p, const char *file, int line, uint32_t n, ...);
 };
 
 
 PacketTest::PacketTest ()
   : Test ("Packet") {}
 
+bool
+PacketTest::DoCheck (Ptr<const Packet> p, const char *file, int line, uint32_t n, ...)
+{
+  bool result = true;
+  std::vector<struct Expected> expected;
+  va_list ap;
+  va_start (ap, n);
+  for (uint32_t k = 0; k < n; ++k)
+    {
+      uint32_t N = va_arg (ap, uint32_t);
+      uint32_t start = va_arg (ap, uint32_t);
+      uint32_t end = va_arg (ap, uint32_t);
+      expected.push_back (Expected (N, start, end));
+    }
+  va_end (ap);
+
+  TagIterator i = p->GetTagIterator ();
+  uint32_t j = 0;
+  while (i.HasNext () && j < expected.size ())
+    {
+      TagIterator::Item item = i.Next ();
+      struct Expected e = expected[j];
+      std::ostringstream oss;
+      oss << "anon::ATestTag<" << e.n << ">";
+      NS_TEST_ASSERT_EQUAL_FILELINE (item.GetTypeId ().GetName (), oss.str (), file, line);
+      NS_TEST_ASSERT_EQUAL_FILELINE (item.GetStart (), e.start, file, line);
+      NS_TEST_ASSERT_EQUAL_FILELINE (item.GetEnd (), e.end, file, line);
+      ATestTagBase *tag = dynamic_cast<ATestTagBase *> (item.GetTypeId ().GetConstructor () ());
+      NS_TEST_ASSERT (tag != 0);
+      item.GetTag (*tag);
+      NS_TEST_ASSERT (!tag->m_error);
+      delete tag;
+      j++;
+    }
+  return result;
+}
 
 bool
 PacketTest::RunTests (void)
 {
-  bool ok = true;
+  bool result = true;
 
   Ptr<Packet> pkt1 = Create<Packet> (reinterpret_cast<const uint8_t*> ("hello"), 5);
   Ptr<Packet> pkt2 = Create<Packet> (reinterpret_cast<const uint8_t*> (" world"), 6);
@@ -428,25 +656,49 @@
   packet->AddAtEnd (pkt1);
   packet->AddAtEnd (pkt2);
   
-  if (packet->GetSize () != 11)
-    {
-      Failure () << "expected size 11, got " << packet->GetSize () << std::endl;
-      ok = false;
-    }
+  NS_TEST_ASSERT_EQUAL (packet->GetSize (), 11);
 
   std::string msg = std::string (reinterpret_cast<const char *>(packet->PeekData ()),
                                  packet->GetSize ());
-  if (msg != "hello world")
-    {
-      Failure () << "expected 'hello world', got '" << msg << "'" << std::endl;
-      ok = false;
-    }
+  NS_TEST_ASSERT_EQUAL (msg, "hello world");
+
+
+  Ptr<const Packet> p = Create<Packet> (1000);
+
+  p->AddMtag (ATestTag<1> ());
+  CHECK (p, 1, E (1, 0, 1000));
+  Ptr<const Packet> copy = p->Copy ();
+  CHECK (copy, 1, E (1, 0, 1000));
+
+  p->AddMtag (ATestTag<2> ());
+  CHECK (p, 2, E (1, 0, 1000), E(2, 0, 1000));
+  CHECK (copy, 1, E (1, 0, 1000));
 
-  return ok;
+  Ptr<Packet> frag0 = p->CreateFragment (0, 10);
+  Ptr<Packet> frag1 = p->CreateFragment (10, 90);
+  Ptr<const Packet> frag2 = p->CreateFragment (100, 900);
+  frag0->AddMtag (ATestTag<3> ());
+  CHECK (frag0, 3, E (1, 0, 10), E(2, 0, 10), E (3, 0, 10));
+  frag1->AddMtag (ATestTag<4> ());
+  CHECK (frag1, 3, E (1, 0, 90), E(2, 0, 90), E (4, 0, 90));
+  frag2->AddMtag (ATestTag<5> ());
+  CHECK (frag2, 3, E (1, 0, 900), E(2, 0, 900), E (5, 0, 900));
+
+  frag1->AddAtEnd (frag2);
+  CHECK (frag1, 6, E (1, 0, 90), E(2, 0, 90), E (4, 0, 90), E (1, 90, 990), E(2, 90, 990), E (5, 90, 990));
+
+  CHECK (frag0, 3, E (1, 0, 10), E(2, 0, 10), E (3, 0, 10));
+  frag0->AddAtEnd (frag1);
+  CHECK (frag0, 9, 
+         E (1, 0, 10), E(2, 0, 10), E (3, 0, 10),
+         E (1, 10, 100), E(2, 10, 100), E (4, 10, 100), 
+         E (1, 100, 1000), E(2, 100, 1000), E (5, 100, 1000));
+
+  return result;
 }
 
 
-static PacketTest gPacketTest;
+static PacketTest g_packetTest;
 
 }; // namespace ns3
 
--- a/src/common/packet.h	Wed Apr 23 15:01:27 2008 -0700
+++ b/src/common/packet.h	Thu Apr 24 14:52:59 2008 -0700
@@ -27,6 +27,8 @@
 #include "tags.h"
 #include "packet-metadata.h"
 #include "tag.h"
+#include "mtag.h"
+#include "mtag-list.h"
 #include "ns3/callback.h"
 #include "ns3/assert.h"
 #include "ns3/ptr.h"
@@ -34,6 +36,60 @@
 namespace ns3 {
 
 /**
+ * \brief Iterator over the set of tags in a packet
+ */
+class TagIterator
+{
+public:
+  /**
+   * Identifies a set tag and a set of bytes within a packet
+   * to which the tag applies.
+   */
+  class Item
+  {
+  public:
+    /**
+     * \returns the ns3::TypeId associated to this tag.
+     */
+    TypeId GetTypeId (void) const;
+    /**
+     * \returns the index of the first byte tagged by this tag.
+     *
+     * The index is an offset from the start of the packet.
+     */
+    uint32_t GetStart (void) const;
+    /**
+     * \returns the index of the last byte tagged by this tag.
+     *
+     * The index is an offset from the start of the packet.
+     */
+    uint32_t GetEnd (void) const;
+    /**
+     * \param tag the user tag to which the data should be copied.
+     *
+     * Read the requested tag and store it in the user-provided
+     * tag instance. This method will crash if the type of the
+     * tag provided by the user does not match the type of
+     * the underlying tag.
+     */
+    void GetTag (Mtag &tag) const;
+  private:
+    friend class TagIterator;
+    Item (TypeId tid, uint32_t start, uint32_t end, MtagBuffer buffer);
+    TypeId m_tid;
+    uint32_t m_start;
+    uint32_t m_end;
+    MtagBuffer m_buffer;
+  };
+  bool HasNext (void) const;
+  Item Next (void);
+private:
+  friend class Packet;
+  TagIterator (MtagList::Iterator i);
+  MtagList::Iterator m_current;
+};
+
+/**
  * \brief network packets
  *
  * Each network packet contains a byte buffer, a list of tags, and
@@ -330,12 +386,34 @@
    * a different CPU.
    */
   void Deserialize (Buffer buffer);
+
+  /**
+   * \param tag the new tag to add to this packet
+   *
+   * Tag each byte included in this packet with the
+   * new tag.
+   */
+  void AddMtag (const Mtag &tag) const;
+  /**
+   * \returns an iterator over the set of tags included in this packet.
+   */
+  TagIterator GetTagIterator (void) const;
+  /**
+   * \param tag the tag to search in this packet
+   * \returns true if the requested tag type was found, false otherwise.
+   *
+   * If the requested tag type is found, it is copied in the user's 
+   * provided tag instance.
+   */
+  bool FindFirstMatchingTag (Mtag &tag) const;
+
 private:
-  Packet (Buffer buffer, Tags tags, PacketMetadata metadata);
+  Packet (const Buffer &buffer, const Tags &tags, const MtagList &tagList, const PacketMetadata &metadata);
   Packet (const Packet &o);
   Packet &operator = (const Packet &o);
   Buffer m_buffer;
   Tags m_tags;
+  MtagList m_tagList;
   PacketMetadata m_metadata;
   mutable uint32_t m_refCount;
   static uint32_t m_globalUid;
--- a/src/common/wscript	Wed Apr 23 15:01:27 2008 -0700
+++ b/src/common/wscript	Thu Apr 24 14:52:59 2008 -0700
@@ -15,6 +15,9 @@
         'pcap-writer.cc',
         'data-rate.cc',
         'error-model.cc',
+        'mtag.cc',
+        'mtag-list.cc',
+        'mtag-buffer.cc',
         ]
 
     headers = bld.create_obj('ns3header')
@@ -32,4 +35,7 @@
         'pcap-writer.h',
         'data-rate.h',
         'error-model.h',
+        'mtag.h',
+        'mtag-list.h',
+        'mtag-buffer.h',
         ]