diff --git a/src/network/model/packet-tag-list.cc b/src/network/model/packet-tag-list.cc --- a/src/network/model/packet-tag-list.cc +++ b/src/network/model/packet-tag-list.cc @@ -34,6 +34,23 @@ NS_LOG_COMPONENT_DEFINE ("PacketTagList"); +PacketTagList::TagData * +PacketTagList::CreateTagData (size_t dataSize) +{ + NS_ASSERT_MSG (dataSize + < std::numeric_limits::max (), + "Requested TagData size " << dataSize + << " exceeds maximum " + << std::numeric_limits::max () ); + + void * p = std::malloc (sizeof (TagData) + dataSize - 1); + // The matching frees are in RemoveAll and RemoveWriter + + TagData * tag = new (p) TagData; + tag->size = dataSize; + return tag; +} + bool PacketTagList::COWTraverse (Tag & tag, PacketTagList::COWWriter Writer) { @@ -133,10 +150,11 @@ NS_ASSERT (cur != 0); NS_ASSERT (cur->count > 1); cur->count--; // unmerge cur - struct TagData * copy = new struct TagData (); + struct TagData * copy = CreateTagData (cur->size); copy->tid = cur->tid; copy->count = 1; - memcpy (copy->data, cur->data, TagData::MAX_SIZE); + copy->size = cur->size; + memcpy (copy->data, cur->data, copy->size); copy->next = cur->next; // merge into tail copy->next->count++; // mark new merge *prevNext = copy; // point prior list at copy @@ -170,14 +188,14 @@ // found tid bool found = true; - tag.Deserialize (TagBuffer (cur->data, - cur->data + TagData::MAX_SIZE)); + tag.Deserialize (TagBuffer (cur->data, cur->data + cur->size)); *prevNext = cur->next; // link around cur if (preMerge) { // found tid before first merge, so delete cur - delete cur; + cur->~TagData (); + std::free (cur); } else { @@ -217,19 +235,17 @@ if (preMerge) { // found tid before first merge, so just rewrite - tag.Serialize (TagBuffer (cur->data, - cur->data + tag.GetSerializedSize ())); + tag.Serialize (TagBuffer (cur->data, cur->data + cur->size)); } else { // cur is always a merge at this point // need to copy, replace, and link past cur cur->count--; // unmerge cur - struct TagData * copy = new struct TagData (); + struct TagData * copy = CreateTagData (tag.GetSerializedSize ()); copy->tid = tag.GetInstanceTypeId (); copy->count = 1; - tag.Serialize (TagBuffer (copy->data, - copy->data + tag.GetSerializedSize ())); + tag.Serialize (TagBuffer (copy->data, copy->data + copy->size)); copy->next = cur->next; // merge into tail if (copy->next != 0) { @@ -247,15 +263,15 @@ // ensure this id was not yet added for (struct TagData *cur = m_next; cur != 0; cur = cur->next) { - NS_ASSERT_MSG (cur->tid != tag.GetInstanceTypeId (), "Error: cannot add the same kind of tag twice."); + NS_ASSERT_MSG (cur->tid != tag.GetInstanceTypeId (), + "Error: cannot add the same kind of tag twice."); } - struct TagData * head = new struct TagData (); + struct TagData * head = CreateTagData (tag.GetSerializedSize ()); head->count = 1; head->next = 0; head->tid = tag.GetInstanceTypeId (); head->next = m_next; - NS_ASSERT (tag.GetSerializedSize () <= TagData::MAX_SIZE); - tag.Serialize (TagBuffer (head->data, head->data + tag.GetSerializedSize ())); + tag.Serialize (TagBuffer (head->data, head->data + head->size)); const_cast (this)->m_next = head; } @@ -270,7 +286,7 @@ if (cur->tid == tid) { /* found tag */ - tag.Deserialize (TagBuffer (cur->data, cur->data + TagData::MAX_SIZE)); + tag.Deserialize (TagBuffer (cur->data, cur->data + cur->size)); return true; } } diff --git a/src/network/model/packet-tag-list.h b/src/network/model/packet-tag-list.h --- a/src/network/model/packet-tag-list.h +++ b/src/network/model/packet-tag-list.h @@ -119,12 +119,6 @@ * The portion of the list between the first branch and the target is * shared. This portion is copied before the #Remove or #Replace is * performed. - * - * \par Memory Management: - * \n - * Packet tags must serialize to a finite maximum size, see TagData - * - * This documentation entitles the original author to a free beer. */ class PacketTagList { @@ -134,37 +128,23 @@ * * See PacketTagList for a discussion of the data structure. * - * See TagData::TagData_e for a discussion of the size limit on - * tag serialization. + * \internal + * Unfortunately this has to be public, because + * PacketTagIterator::Item::GetTag() needs the data and size values. + * The Item nested class can't be forward declared, so friending isn't + * possible. + * + * We use placement new so we can allocate enough room for the Tag + * type which will be serialized into data. See Object::Aggregates + * for a similar construction. */ struct TagData { - /** - * \brief Packet Tag maximum size - * - * The maximum size (in bytes) of a Tag is stored - * in this constant. - * - * \internal - * Ideally, TagData would be 32 bytes in size, so they require - * no padding on 64-bit architectures. (The architecture - * affects the size because of the \c #next pointer.) - * This would leave 18 bytes for \c #data. However, - * ns3:Ipv6PacketInfoTag needs 19 bytes! The current - * implementation allows 20 bytes, which gives TagData - * a size of 30 bytes on 32-byte machines (which gets - * padded with 2 bytes), and 34 bytes on 64-bit machines, which - * gets padded to 40 bytes. - */ - enum TagData_e - { - MAX_SIZE = 21 /**< Size of serialization buffer #data */ - }; - - uint8_t data[MAX_SIZE]; /**< Serialization buffer */ - struct TagData * next; /**< Pointer to next in list */ - TypeId tid; /**< Type of the tag serialized into #data */ - uint32_t count; /**< Number of incoming links */ + struct TagData * next; /**< Pointer to next in list */ + uint32_t count; /**< Number of incoming links */ + TypeId tid; /**< Type of the tag serialized into #data */ + uint32_t size; /**< Size of the \c data buffer */ + uint8_t data[1]; /**< Serialization buffer */ }; /* struct TagData */ /** @@ -240,6 +220,16 @@ private: /** + * Allocate and construct a TagData struct, sizing the data area + * large enough to serialize dataSize bytes from a Tag. + * + * \param [in] dataSize The serialized size of the Tag. + * \returns The newly constructed TagData object. + */ + static + TagData * CreateTagData (size_t dataSize); + + /** * Typedef of method function pointer for copy-on-write operations * * \param [in] tag The tag type to operate on. @@ -272,7 +262,7 @@ * pointing to \pname{cur}. * \returns True, since tag will definitely be removed. */ - bool RemoveWriter (Tag & tag, bool preMerge, + bool RemoveWriter (Tag & tag, bool preMerge, struct TagData * cur, struct TagData ** prevNext); /** * Copy-on-write implementing Replace @@ -285,7 +275,8 @@ * pointing to \pname{cur}. * \returns True, since tag value will definitely be replaced. */ - bool ReplaceWriter (Tag & tag, bool preMerge, struct TagData * cur, struct TagData ** prevNext); + bool ReplaceWriter (Tag & tag, bool preMerge, + struct TagData * cur, struct TagData ** prevNext); /** * Pointer to first \ref TagData on the list @@ -350,13 +341,15 @@ } if (prev != 0) { - delete prev; + prev->~TagData (); + std::free (prev); } prev = cur; } if (prev != 0) { - delete prev; + prev->~TagData (); + std::free (prev); } m_next = 0; } diff --git a/src/network/model/packet.cc b/src/network/model/packet.cc --- a/src/network/model/packet.cc +++ b/src/network/model/packet.cc @@ -113,8 +113,7 @@ { NS_ASSERT (tag.GetInstanceTypeId () == m_data->tid); tag.Deserialize (TagBuffer ((uint8_t*)m_data->data, - (uint8_t*)m_data->data - + PacketTagList::TagData::MAX_SIZE)); + (uint8_t*)m_data->data + m_data->size)); } diff --git a/src/network/utils/packet-socket.cc b/src/network/utils/packet-socket.cc --- a/src/network/utils/packet-socket.cc +++ b/src/network/utils/packet-socket.cc @@ -643,7 +643,6 @@ DeviceNameTag::GetSerializedSize (void) const { uint32_t s = 1 + m_deviceName.size(); // +1 for name length field - s = std::min (s, (uint32_t)PacketTagList::TagData::MAX_SIZE); return s; } void @@ -652,8 +651,6 @@ const char *n = m_deviceName.c_str(); uint8_t l = (uint8_t) m_deviceName.size (); - l = std::min ((uint32_t)l, (uint32_t)PacketTagList::TagData::MAX_SIZE - 1); - i.WriteU8 (l); i.Write ( (uint8_t*) n , (uint32_t) l); }