src/common/packet-history.cc
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Thu, 31 May 2007 14:52:15 +0200
changeset 807 1c3cc23a0cb8
parent 806 0190f1b49dc8
child 809 1bfeec93c1e1
permissions -rw-r--r--
force use of the ItemList to test it

/* -*- 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 "packet-history.h"
#include "chunk.h"
#include "buffer.h"

namespace {

class ItemList
{
public:
  void InitPayload (uint32_t packetUid, uint32_t size);
  void AddHeader (uint32_t type, uint32_t size);
  void AddTrailer (uint32_t type, uint32_t size);

  void RemHeader (uint32_t type, uint32_t size);
  void RemTrailer (uint32_t type, uint32_t size);

  void RemAtStart (uint32_t toRemove);
  void RemAtEnd (uint32_t toRemove);

  void AddAtEnd (ItemList const *other);

  void Print (std::ostream &os, ns3::Buffer buffer, const ns3::PacketPrinter &printer) const;

private:
  enum Type {
    PAYLOAD,
    HEADER,
    TRAILER,
  };
  struct Item
  {
    enum ItemList::Type m_type;
    uint32_t m_size;
    uint32_t m_chunkType;
    uint32_t m_fragmentStart;
    uint32_t m_fragmentEnd;
    uint32_t m_packetUid;
  };
  std::list<Item> m_itemList;
};

void 
ItemList::InitPayload (uint32_t packetUid, uint32_t size)
{
  NS_ASSERT (m_itemList.empty ());
  struct Item item;
  item.m_type = ItemList::PAYLOAD;
  item.m_chunkType = 0;
  item.m_size = size;
  item.m_fragmentStart = 0;
  item.m_fragmentEnd = item.m_size;
  item.m_packetUid = packetUid;
  m_itemList.push_back (item);
}

void 
ItemList::AddHeader (uint32_t type, uint32_t size)
{
  struct Item item;
  item.m_type = ItemList::HEADER;
  item.m_chunkType = type;
  item.m_size = size;
  item.m_fragmentStart = 0;
  item.m_fragmentEnd = size;
  item.m_packetUid = m_itemList.front ().m_packetUid;
  m_itemList.push_front (item);
}

void 
ItemList::AddTrailer (uint32_t type, uint32_t size)
{
  struct Item item;
  item.m_type = ItemList::TRAILER;
  item.m_chunkType = type;
  item.m_size = size;
  item.m_fragmentStart = 0;
  item.m_fragmentEnd = size;
  item.m_packetUid = m_itemList.back ().m_packetUid;
  m_itemList.push_back (item);
}

void 
ItemList::RemHeader (uint32_t type, uint32_t size)
{
  struct Item item = m_itemList.front ();
  if (item.m_type != ItemList::HEADER ||
      item.m_size != size ||
      item.m_chunkType != type)
    {
      NS_FATAL_ERROR ("Removing Unexpected header");
    }
  else if (item.m_fragmentStart != 0 ||
           item.m_fragmentEnd != item.m_size)
    {
      NS_FATAL_ERROR ("Removing non-complete header");
    }
  m_itemList.pop_front ();
}
void 
ItemList::RemTrailer (uint32_t type, uint32_t size)
{
  struct Item item = m_itemList.back ();
  if (item.m_type != ItemList::TRAILER ||
      item.m_size != size ||
      item.m_chunkType != type)
    {
      NS_FATAL_ERROR ("Removing Unexpected trailer");
    }
  else if (item.m_fragmentStart != 0 ||
           item.m_fragmentEnd != item.m_size)
    {
      NS_FATAL_ERROR ("Removing non-complete trailer");
    }
  m_itemList.pop_back ();
}

void 
ItemList::RemAtStart (uint32_t toRemove)
{
  uint32_t leftToRemove = toRemove;
  while (!m_itemList.empty () && leftToRemove > 0)
    {
      struct Item &item = m_itemList.front ();
      if (item.m_size >= leftToRemove)
        {
          m_itemList.pop_front ();
          leftToRemove -= item.m_size;
        }
      else
        {
          item.m_size -= leftToRemove;
          item.m_fragmentStart += leftToRemove;
          leftToRemove = 0;
          NS_ASSERT (item.m_size == item.m_fragmentEnd - item.m_fragmentStart &&
                     item.m_fragmentStart <= item.m_fragmentEnd);
        }
    }
  NS_ASSERT (leftToRemove == 0);
}
void 
ItemList::RemAtEnd (uint32_t toRemove)
{
  uint32_t leftToRemove = toRemove;
  while (!m_itemList.empty () && leftToRemove > 0)
    {
      struct Item &item = m_itemList.back ();
      if (item.m_size >= leftToRemove)
        {
          m_itemList.pop_back ();
          leftToRemove -= item.m_size;
        }
      else
        {
          item.m_size -= leftToRemove;
          item.m_fragmentEnd -= leftToRemove;
          leftToRemove = 0;
        }
      NS_ASSERT (item.m_size == item.m_fragmentEnd - item.m_fragmentStart &&
                 item.m_fragmentStart <= item.m_fragmentEnd &&
                 item.m_fragmentEnd <= item.m_size);
    }
  NS_ASSERT (leftToRemove == 0);
}

void 
ItemList::AddAtEnd (ItemList const *other)
{
  for (std::list<ItemList::Item>::const_iterator i = other->m_itemList.begin (); 
       i != other->m_itemList.end (); i++)
    {
      const ItemList::Item &item = *i;
      ItemList::Item &last = m_itemList.back ();
      if (item.m_packetUid == last.m_packetUid &&
          item.m_type == last.m_type &&
          item.m_chunkType == last.m_chunkType &&
          item.m_size == last.m_size &&
          last.m_fragmentEnd != last.m_size && 
          item.m_fragmentStart == last.m_fragmentEnd)
        {
          last.m_fragmentEnd = item.m_fragmentEnd;
        }
      else
        {
          m_itemList.push_back (item);
        }
    }
}

void 
ItemList::Print (std::ostream &os, ns3::Buffer buffer, const ns3::PacketPrinter &printer) const
{
  NS_ASSERT (!m_itemList.empty ());
  uint32_t totalSize = 0;
  for (std::list<ItemList::Item>::const_iterator i = m_itemList.begin (); 
       i != m_itemList.end (); i++)
    {
      ItemList::Item item = *i;
      totalSize += item.m_fragmentEnd - item.m_fragmentStart;
    }
  NS_ASSERT (totalSize == buffer.GetSize ());
  uint32_t offset = 0;
  for (std::list<ItemList::Item>::const_iterator i = m_itemList.begin (); 
       i != m_itemList.end (); i++)
    {
      ItemList::Item item = *i;
      if (item.m_type == ItemList::PAYLOAD)
        {
          printer.PrintPayload (os, item.m_packetUid, item.m_size, item.m_fragmentStart, item.m_fragmentEnd);
        }
      else if (item.m_fragmentStart != 0 || 
               item.m_fragmentEnd != item.m_size)
        {
          printer.PrintChunkFragment (item.m_chunkType, os, item.m_packetUid, item.m_size, 
                                      item.m_fragmentStart, item.m_fragmentEnd);
        }
      else if (item.m_type == ItemList::HEADER)
        {
          ns3::Buffer::Iterator j = buffer.Begin ();
          j.Next (offset);
          printer.PrintChunk (item.m_chunkType, j, os, item.m_packetUid, item.m_size);
        }
      else if (item.m_type == ItemList::TRAILER)
        {
          ns3::Buffer::Iterator j = buffer.End ();
          j.Prev (totalSize - offset + item.m_size);
          printer.PrintChunk (item.m_chunkType, j, os, item.m_packetUid, item.m_size);
        }
      else 
        {
          NS_ASSERT (false);
        }
      offset += item.m_fragmentEnd - item.m_fragmentStart;
    }
}

} // anonymous namespace

namespace ns3 {

bool PacketHistory::m_enable = false;
uint32_t PacketHistory::m_maxSize = 0;
PacketHistory::DataFreeList PacketHistory::m_freeList;

void 
PacketHistory::Enable (void)
{
  m_enable = true;
}


PacketHistory::PacketHistory ()
  : m_data (0),
    m_end (0),
    m_n (0),
    m_aggregated (false)
{
  Construct (0, 0);
}
PacketHistory::PacketHistory (uint32_t uid, uint32_t size)
  : m_data (0),
    m_end (0),
    m_n (0),
    m_aggregated (false)
{
  Construct (uid, size);
}
void
PacketHistory::Construct (uint32_t uid, uint32_t size)
{
  if (m_enable) 
    {
      m_data = PacketHistory::Create (size);
      AppendOneCommand (PacketHistory::INIT_UID,
                        uid);
    }
}
PacketHistory::PacketHistory (PacketHistory const &o)
  : m_data (o.m_data),
    m_end (o.m_end),
    m_n (o.m_n),
    m_aggregated (o.m_aggregated)
{
  if (m_data != 0) 
    {
      m_data->m_count++;
    }
}
PacketHistory &
PacketHistory::operator = (PacketHistory const& o)
{
  if (m_data == o.m_data) 
    {
      // self assignment
      return *this;
    }
  if (m_data != 0) 
    {
      m_data->m_count--;
      if (m_data->m_count == 0) 
        {
          PacketHistory::Recycle (m_data);
        }
    }
  m_data = o.m_data;
  m_end = o.m_end;
  m_n = o.m_n;
  m_aggregated = o.m_aggregated;
  if (m_data != 0) 
    {
      m_data->m_count++;
    }
  return *this;
}
PacketHistory::~PacketHistory ()
{
  if (m_data != 0) 
    {
      m_data->m_count--;
      if (m_data->m_count == 0) 
        {
          PacketHistory::Recycle (m_data);
        }
    }
}


struct PacketHistory::CommandData *
PacketHistory::Create (uint32_t size)
{
  if (size > m_maxSize)
    {
      m_maxSize = size;
    }
  while (!m_freeList.empty ()) 
    {
      struct PacketHistory::CommandData *data = m_freeList.back ();
      m_freeList.pop_back ();
      if (data->m_size >= size) 
        {
          data->m_count = 1;
          return data;
        }
      PacketHistory::Deallocate (data);
    }
  return PacketHistory::Allocate (size);
}

void
PacketHistory::Recycle (struct CommandData *data)
{
  NS_ASSERT (data->m_count == 0);
  if (m_freeList.size () > 1000 ||
      data->m_size < m_maxSize) 
    {
      PacketHistory::Deallocate (data);
    } 
  else 
    {
      m_freeList.push_back (data);
    }
}

struct PacketHistory::CommandData *
PacketHistory::Allocate (uint32_t n)
{
  uint32_t size = sizeof (struct CommandData);
  if (n <= 4)
    {
      n = 4;
    }
  size += (n-4) * (4 + 1);
  uint8_t *buf = new uint8_t [size];
  struct CommandData *data = (struct CommandData *)buf;
  data->m_size = n;
  data->m_count = 1;
  return data;
}
void 
PacketHistory::Deallocate (struct CommandData *data)
{
  uint8_t *buf = (uint8_t *)data;
  delete [] buf;
}

void
PacketHistory::AppendOneCommand (uint32_t type, uint32_t data0, uint32_t data1)
{
  NS_ASSERT (m_data != 0);
  uint32_t n = GetUleb128Size (type);
  n += GetUleb128Size (data0);
  n += GetUleb128Size (data1);

  if (m_data->m_size > m_end + n)
    {
      if (m_data->m_count == 1 ||
          m_data->m_dirtyEnd == m_end)
        {
          AppendValue (data1);
          AppendValue (data0);
          AppendValue (type);
          m_n++;
          return;
        }
      else
        {
          uint8_t *buffer = &(m_data->m_data[m_end]);
          uint32_t lastType = ReadReverseValue (&buffer);
          if (lastType == type)
            {
              uint32_t lastData = ReadReverseValue (&buffer);
              if (lastData == data0)
                {
                  lastData = ReadReverseValue (&buffer);
                  if (lastData == data1)
                    {
                      return;
                    }
                }
            }
        }
    }
  Reserve (n);
  AppendValue (data1);
  AppendValue (data0);
  AppendValue (type);
  m_n++;
}

void
PacketHistory::AppendOneCommand (uint32_t type, uint32_t data)
{
  NS_ASSERT (m_data != 0);
  uint32_t n = GetUleb128Size (data);
  n += GetUleb128Size (type);

  if (m_data->m_size > m_end + n)
    {
      if (m_data->m_count == 1 ||
          m_data->m_dirtyEnd == m_end)
        {
          AppendValue (data);
          AppendValue (type);
          m_n++;
          return;
        }
      else
        {
          uint8_t *buffer = &(m_data->m_data[m_end]);
          uint32_t lastType = ReadReverseValue (&buffer);
          if (lastType == type)
            {
              uint32_t lastData = ReadReverseValue (&buffer);
              if (lastData == data)
                {
                  return;
                }
            }
        }
    }
  Reserve (n);
  AppendValue (data);
  AppendValue (type);
  m_n++;
}
void
PacketHistory::ReserveCopy (uint32_t size)
{
  struct CommandData *newData = PacketHistory::Create (m_end + size);
  memcpy (newData->m_data, m_data->m_data, m_end);
  newData->m_dirtyEnd = m_end;
  m_data->m_count--;
  if (m_data->m_count == 0) 
    {
      PacketHistory::Recycle (m_data);
    }
  m_data = newData;
}
void
PacketHistory::Reserve (uint32_t size)
{
  NS_ASSERT (m_data != 0);
  if (m_data->m_size > m_end + size &&
      (m_data->m_count == 1 ||
       m_data->m_dirtyEnd == m_end))
    {
      /* enough room, not dirty. */
    }
  else 
    {
      /* (enough room and dirty) or (not enough room) */
      ReserveCopy (size);
    }
}

uint32_t 
PacketHistory::GetUleb128Size (uint32_t value) const
{
  uint32_t n = 0;
  uint32_t tmp = value;
  do {
    tmp >>= 7;
    n++;
  } while (tmp != 0);
  return n;
}

uint32_t 
PacketHistory::GetReverseUleb128Size (uint8_t *buffer) const
{
  uint8_t *cur = buffer;
  uint8_t byte = *cur;
  if (byte & 0x80)
    {
      NS_FATAL_ERROR ("The last byte should have a zero high bit");
    }
  cur--;
  while ((byte & 0x80) && (buffer - cur) < 5)
    {
      cur--;
      byte = *cur;
    }
  uint32_t n = buffer - cur;
  return n;
}

void
PacketHistory::AppendValue (uint32_t value)
{
  uint32_t n = 0;
  uint8_t *buffer = &m_data->m_data[m_end];
  do {
    uint8_t byte = value & (~0x80);
    value >>= 7;
    if (value != 0)
      {
        /* more bytes to come */
        byte |= 0x80;
      }
    n++;
    *buffer = byte;
    buffer++;
  } while (value != 0);
  m_end += n;
  if (m_end > m_data->m_dirtyEnd)
    {
      m_data->m_dirtyEnd = m_end;
    }
}

uint32_t
PacketHistory::ReadValue (uint8_t *buffer, uint32_t *n) const
{
  uint32_t result = 0;
  uint8_t shift, byte;
  result = 0;
  shift = 0;
  do {
    byte = *buffer;
    buffer++;
    result |= (byte & (~0x80))<<shift;
    shift += 7;
    (*n)++;
  } while (byte & 0x80 && 
           /* a LEB128 unsigned number is at most 5 bytes long. */
           shift < (7*5)); 
  if (byte & 0x80)
    {
      /* This means that the LEB128 number was not valid.
       * ie: the last (5th) byte did not have the high-order bit zeroed.
       */
      result = 0xffffffff;
    }
  return result;
}

uint32_t
PacketHistory::ReadReverseValue (uint8_t **pBuffer) const
{
  uint32_t n = GetReverseUleb128Size (*pBuffer);
  NS_ASSERT (n > 0);
  uint8_t *buffer = *pBuffer - n + 1;
  uint32_t read = 0;
  uint32_t result = ReadValue (buffer, &read);
  NS_ASSERT (read == n);
  *pBuffer = *pBuffer - n;
  return result;
}

uint32_t
PacketHistory::ReadForwardValue (uint8_t **pBuffer) const
{
  uint32_t read = 0;
  uint32_t result = ReadValue (*pBuffer, &read);
  *pBuffer = *pBuffer + read;
  return result;
}

PacketHistory 
PacketHistory::CreateFragment (uint32_t start, uint32_t end) const
{
  PacketHistory fragment = *this;
  fragment.RemoveAtStart (start);
  fragment.RemoveAtEnd (end);
  return fragment;
}

void 
PacketHistory::AddHeader (uint32_t uid, Chunk const & header, uint32_t size)
{
  if (m_enable) 
    {
      AppendOneCommand (PacketHistory::ADD_HEADER, uid, size);
    }  
}
void 
PacketHistory::RemoveHeader (uint32_t uid, Chunk const & header, uint32_t size)
{
  if (m_enable) 
    {
      AppendOneCommand (PacketHistory::REM_HEADER, uid, size);
    }
}
void 
PacketHistory::AddTrailer (uint32_t uid, Chunk const & trailer, uint32_t size)
{
  if (m_enable) 
    {
      AppendOneCommand (PacketHistory::ADD_TRAILER, uid, size);
    }
}
void 
PacketHistory::RemoveTrailer (uint32_t uid, Chunk const & trailer, uint32_t size)
{
  if (m_enable) 
    {
      AppendOneCommand (PacketHistory::REM_TRAILER, uid, size);
    }
}
void
PacketHistory::AddAtEnd (PacketHistory const&o)
{
  if (m_enable) 
    {
      m_aggregated = true;
      uint32_t n = GetUleb128Size (PacketHistory::ADD_AT_END);
      n += GetUleb128Size (o.m_end); 
      n += o.m_end;
      Reserve (n);
      memcpy (&m_data->m_data[m_end], o.m_data->m_data, o.m_end);
      m_end += o.m_end;
      if (m_end > m_data->m_dirtyEnd)
        {
          m_data->m_dirtyEnd = m_end;
        }
      AppendOneCommand (PacketHistory::ADD_AT_END, o.m_end);
    }
}
void
PacketHistory::AddPaddingAtEnd (uint32_t end)
{
  if (m_enable)
    {
      AppendOneCommand (PacketHistory::PADDING_AT_END, end);
    }
}
void 
PacketHistory::RemoveAtStart (uint32_t start)
{
  if (m_enable) 
    {
      AppendOneCommand (PacketHistory::REM_AT_START, start);
    }
}
void 
PacketHistory::RemoveAtEnd (uint32_t end)
{
  if (m_enable) 
    {
      AppendOneCommand (PacketHistory::REM_AT_END, end);
    }
}

void 
PacketHistory::PrintSimple (std::ostream &os, Buffer buffer, const PacketPrinter &printer) const
{
  Buffer original = buffer;
  HeadersToPrint headersToPrint;
  TrailersToPrint trailersToPrint;
  uint8_t *dataBuffer = &m_data->m_data[m_end] - 1;
  int32_t start = 0;
  int32_t end = buffer.GetSize ();
  int32_t curStart = start;
  int32_t curEnd = end;
  for (uint32_t i = 0; i < m_n; i++)
    {
      uint32_t type = ReadReverseValue (&dataBuffer);
      uint32_t data = ReadReverseValue (&dataBuffer);
      switch (type)
        {
        case PacketHistory::INIT_UID:
          break;
        case PacketHistory::INIT_SIZE:
          std::cout << "init size=" << data << std::endl;
          break;
        case PacketHistory::ADD_HEADER: {
          int32_t size = ReadReverseValue (&dataBuffer);
          if (curStart == start)
            {
              if (start + size < end)
                {
                  headersToPrint.push_back (std::make_pair (data, curStart));
                }
              curStart += size;
              start += size;
            }
          else if (curStart + size <= start)
            {
              // header lies entirely outside of data area.
              curStart += size;
            }
          else if (curStart < start)
            {
              // header lies partly inside and outside of data area
              // potentially, because we fragmented the packet in the middle
              // of this header.
              curStart += size;
            }
          else
            {
              // header lies entirely inside data area but there is some 
              // data at the start of the data area which does not belong
              // to this header. Potentially, because we fragmented
              // the packet in the middle of a previous header.
              NS_ASSERT (curStart > start);
              // we print the content of the header anyway because we can.
              if (start + size < end)
                {
                  headersToPrint.push_back (std::make_pair (data, curStart));
                }
              curStart += size;
              start = curStart;
            }
        } break;
        case PacketHistory::REM_HEADER: {
          int32_t size = ReadReverseValue (&dataBuffer);
          if (curStart <= start)
            {
              // header lies entirely outside of data area.
              curStart -= size;
            }
          else
            {
              NS_ASSERT (false);
            }
        } break;
        case PacketHistory::ADD_TRAILER: {
          int32_t size = ReadReverseValue (&dataBuffer);
          if (curEnd == end)
            {
              if (end - size >= start)
                {
                  // trailer lies exactly at the end of the data area
                  trailersToPrint.push_back (std::make_pair (data, buffer.GetSize () - curEnd));
                }
              curEnd -= size;
              end -= size;
            }
          else if (curEnd - size >= end)
            {
              // trailer lies entirely outside of data area.
              curEnd -= size;
            }
          else if (curEnd > end)
            {
              // header lies partly inside and partly outside of
              // data area, potentially because of fragmentation.
              curEnd -= size;
            }
          else
            {
              // header lies entirely inside data area
              NS_ASSERT (curEnd < end);
              if (end - size >= start)
                {
                  trailersToPrint.push_back (std::make_pair (data, buffer.GetSize () - curEnd));
                }
              curEnd -= size;
              end = curEnd;

            }
        } break;
        case PacketHistory::REM_TRAILER: {
          int32_t size = ReadReverseValue (&dataBuffer);
          if (curEnd >= end)
            {
              curEnd += size;
            }
          else
            {
              NS_ASSERT (false);
            }
        } break;
        case PacketHistory::REM_AT_START:
          curStart -= data;
          break;
        case PacketHistory::REM_AT_END:
          curEnd += data;
          break;
        }
    }
  for (HeadersToPrint::iterator j = headersToPrint.begin (); 
       j != headersToPrint.end (); j++)
    {
      uint32_t uid = j->first;
      uint32_t offset = j->second;
      Buffer::Iterator tmp = original.Begin ();
      tmp.Next (offset);
      printer.PrintChunk (uid, tmp, os, 0, 0);
    }
  for (TrailersToPrint::reverse_iterator j = trailersToPrint.rbegin (); 
       j != trailersToPrint.rend (); j++)
    {
      uint32_t uid = j->first;
      uint32_t offset = j->second;
      Buffer::Iterator tmp = original.End ();
      tmp.Prev (offset);
      printer.PrintChunk (uid, tmp, os, 0, 0);
    }
}

void 
PacketHistory::PrintComplex (std::ostream &os, Buffer buffer, const PacketPrinter &printer) const
{
  // we need to build a linked list of the different fragments 
  // which are stored in this packet.
  uint8_t *dataBuffer = &m_data->m_data[0];
  ItemList itemList;
  BuildItemList (&itemList, dataBuffer, m_end);
  itemList.Print (os, buffer, printer);
}

void 
PacketHistory::BuildItemList (ItemList *list, uint8_t *buffer, uint32_t size) const
{
  // we need to build a linked list of the different fragments 
  // which are stored in this packet.
  uint8_t *dataBuffer = buffer;
  ItemList itemList;
  for (uint32_t i = 0; i < m_n; i++)
    {
      uint32_t type = ReadForwardValue (&dataBuffer);
      uint32_t data = ReadForwardValue (&dataBuffer);
      switch (type)
        {
        case INIT_UID:
          break;
        case INIT_SIZE: {
          itemList.InitPayload (0, data);
        } break;
        case ADD_HEADER: {
          uint32_t size = ReadForwardValue (&dataBuffer);
          itemList.AddHeader (data, size);
        } break;
        case REM_HEADER: {
          uint32_t size = ReadForwardValue (&dataBuffer);
          itemList.RemHeader (data, size);
        } break;
        case ADD_TRAILER: {
          uint32_t size = ReadForwardValue (&dataBuffer);
          itemList.AddTrailer (data, size);
        } break;
        case REM_TRAILER: {
          uint32_t size = ReadForwardValue (&dataBuffer);
          itemList.RemTrailer (data, size);
        } break;
        case ADD_AT_END: {
          ItemList other;
          BuildItemList (&other, dataBuffer, data);
          itemList.AddAtEnd (&other);
        } break;
        case REM_AT_START: {
          itemList.RemAtStart (data);
        } break;
        case REM_AT_END: {
          itemList.RemAtEnd (data);
        } break;
        case PADDING_AT_END:
          break;
        }
    }  
}

void 
PacketHistory::PrintDefault (std::ostream &os, Buffer buffer) const
{
  Print (os, buffer, PacketPrinter::GetDefault ());
}

void
PacketHistory::Print (std::ostream &os, Buffer buffer, const PacketPrinter &printer) const
{
  if (!m_enable) 
    {
      return;
    }

  if (true)
    {
      PrintComplex (os, buffer, printer);
    }
  else
    {
      PrintSimple (os, buffer, printer);
    }
}



}; // 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.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 PacketHistoryTest : public Test {
public:
  PacketHistoryTest ();
  virtual ~PacketHistoryTest ();
  bool CheckHistory (Packet p, 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 m_headerError;
  bool m_trailerError;
  std::list<int> m_prints;
  PacketPrinter m_printer;
};

PacketHistoryTest::PacketHistoryTest ()
  : Test ("PacketHistory")
{
  m_printer.AddPayloadPrinter (MakeCallback (&PacketHistoryTest::PrintPayload, this));
  m_printer.AddDefaultPrinter (MakeCallback (&PacketHistoryTest::PrintDefault, this));
}

PacketHistoryTest::~PacketHistoryTest ()
{}

template <int N>
void 
PacketHistoryTest::RegisterHeader (void)
{
  static bool registered = false;
  if (!registered)
    {
      m_printer.AddPrinter (MakeCallback (&PacketHistoryTest::PrintHeader<N>, this),
                            MakeCallback (&PacketHistoryTest::PrintFragment, this));
      registered = true;
    }
}

template <int N>
void 
PacketHistoryTest::RegisterTrailer (void)
{
  static bool registered = false;
  if (!registered)
    {
      m_printer.AddPrinter (MakeCallback (&PacketHistoryTest::PrintTrailer<N>, this),
                            MakeCallback (&PacketHistoryTest::PrintFragment, this));
      registered = true;
    }
}


template <int N>
void 
PacketHistoryTest::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 
PacketHistoryTest::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 
PacketHistoryTest::PrintFragment (std::ostream &os,uint32_t packetUid,
                                  uint32_t size,std::string & name, 
                                  struct PacketPrinter::FragmentInformation info)
{
  NS_ASSERT (false);
}
void 
PacketHistoryTest::PrintDefault (std::ostream& os,uint32_t packetUid,
                     uint32_t size,std::string& name,
                     struct PacketPrinter::FragmentInformation info)
{
  NS_ASSERT (false);
}
void 
PacketHistoryTest::PrintPayload (std::ostream &os,uint32_t packetUid,
                                 uint32_t size,
                                 struct PacketPrinter::FragmentInformation info)
{
  NS_ASSERT (false);
}


void 
PacketHistoryTest::CleanupPrints (void)
{
  m_prints.clear ();
}

bool 
PacketHistoryTest::CheckHistory (Packet p, char *file, int line, uint32_t n, ...)
{
  m_headerError = false;
  m_trailerError = false;
  va_list ap;
  p.Print (std::cerr, m_printer);
  va_start (ap, n);
  if (m_headerError)
    {
      std::cout << "PacketHistory header error. file=" << file 
                << ", line=" << line << std::endl;
      return false;
    }
  if (m_trailerError)
    {
      std::cout << "PacketHistory trailer error. file=" << file 
                << ", line=" << line << std::endl;
      return false;
    }
  if (n != m_prints.size ())
    {
      goto error;
    }
  for (std::list<int>::iterator i = m_prints.begin (); 
       i != m_prints.end (); i++)
    {
      int v = va_arg (ap, int);
      if (v != *i)
        {
          va_end (ap);
          goto error;
        }
    }
  va_end (ap);
  return true;
 error:
  std::cout << "PacketHistory error. file="<< file 
            << ", line=" << line << ", got:\"";
  for (std::list<int>::iterator i = m_prints.begin (); 
       i != m_prints.end (); i++)
    {
      std::cout << *i << ", ";
    }
  std::cout << "\", expected: \"";
  va_start (ap, n);
  for (uint32_t j = 0; j < n; j++)
    {
      int v = va_arg (ap, int);
      std::cout << v << ", ";
    }
  va_end (ap);
  std::cout << "\"" << std::endl;
  return false;
}

#define ADD_HEADER(p, n)                        \
  {                                             \
    HistoryHeader<n> header;                    \
    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;                             \
      }                                         \
    CleanupPrints ();                           \
  }

bool
PacketHistoryTest::RunTests (void)
{
  bool ok = true;

  PacketHistory::Enable ();

  Packet p = Packet (0);
  Packet p1 = Packet (0);

  p = Packet (10);
  ADD_TRAILER (p, 100);
  CHECK_HISTORY (p, 1, 100);

  p = Packet (10);
  ADD_HEADER (p, 1);
  ADD_HEADER (p, 2);
  ADD_HEADER (p, 3);
  CHECK_HISTORY (p, 3, 
                 3, 2, 1);
  ADD_HEADER (p, 5);
  CHECK_HISTORY (p, 4, 
                 5, 3, 2, 1);
  ADD_HEADER (p, 6);
  CHECK_HISTORY (p, 5, 
                 6, 5, 3, 2, 1);

  p = Packet (10);
  ADD_HEADER (p, 1);
  ADD_HEADER (p, 2);
  ADD_HEADER (p, 3);
  REM_HEADER (p, 3);
  CHECK_HISTORY (p, 2, 
                 2, 1);

  p = Packet (10);
  ADD_HEADER (p, 1);
  ADD_HEADER (p, 2);
  ADD_HEADER (p, 3);
  REM_HEADER (p, 3);
  REM_HEADER (p, 2);
  CHECK_HISTORY (p, 1, 
                 1);

  p = Packet (10);
  ADD_HEADER (p, 1);
  ADD_HEADER (p, 2);
  ADD_HEADER (p, 3);
  REM_HEADER (p, 3);
  REM_HEADER (p, 2);
  REM_HEADER (p, 1);
  CHECK_HISTORY (p, 0);

  p = Packet (10);
  ADD_HEADER (p, 1);
  ADD_HEADER (p, 2);
  ADD_HEADER (p, 3);
  p1 = p;
  REM_HEADER (p1, 3);
  REM_HEADER (p1, 2);
  REM_HEADER (p1, 1);
  CHECK_HISTORY (p1, 0);
  CHECK_HISTORY (p, 3, 
                 3, 2, 1);
  ADD_HEADER (p1, 1);
  ADD_HEADER (p1, 2);
  CHECK_HISTORY (p1, 2, 
                 2, 1);
  CHECK_HISTORY (p, 3, 
                 3, 2, 1);
  ADD_HEADER (p, 3);
  CHECK_HISTORY (p, 4, 
                 3, 3, 2, 1);
  ADD_TRAILER (p, 4);
  CHECK_HISTORY (p, 5, 
                 3, 3, 2, 1, 4);
  ADD_TRAILER (p, 5);
  CHECK_HISTORY (p, 6, 
                 3, 3, 2, 1, 4, 5);
  REM_HEADER (p, 3);
  CHECK_HISTORY (p, 5, 
                 3, 2, 1, 4, 5);
  REM_TRAILER (p, 5);
  CHECK_HISTORY (p, 4, 
                 3, 2, 1, 4);
  p1 = p;
  REM_TRAILER (p, 4);
  CHECK_HISTORY (p, 3, 
                 3, 2, 1);
  CHECK_HISTORY (p1, 4, 
                 3, 2, 1, 4);
  p1.RemoveAtStart (3);
  CHECK_HISTORY (p1, 3, 
                 2, 1, 4);
  p1.RemoveAtStart (2);
  CHECK_HISTORY (p1, 2, 
                 1, 4);
  p1.RemoveAtEnd (4);
  CHECK_HISTORY (p1, 1, 
                 1);
  p1.RemoveAtStart (1);
  CHECK_HISTORY (p1, 0);

  p = Packet (10);
  ADD_HEADER (p, 8);
  ADD_TRAILER (p, 8);
  ADD_TRAILER (p, 8);
  p.RemoveAtStart (8+10+8);
  CHECK_HISTORY (p, 1, 8);


  p = Packet (10);
  ADD_HEADER (p, 10);
  ADD_HEADER (p, 8);
  ADD_TRAILER (p, 6);
  ADD_TRAILER (p, 7);
  ADD_TRAILER (p, 9);
  p.RemoveAtStart (5);
  p.RemoveAtEnd (12);
  CHECK_HISTORY (p, 2, 10, 6);


  return ok;
}

static PacketHistoryTest g_packetHistoryTest;

}//namespace ns3