# HG changeset patch # User Tommaso Pecorella # Date 1531573115 -7200 # Parent 0c09afa2dbb2143188a6fc979d98443ade13a79f New Bit[Des,S]erializer classes diff --git a/src/network/test/bit-serializer-test.cc b/src/network/test/bit-serializer-test.cc new file mode 100644 --- /dev/null +++ b/src/network/test/bit-serializer-test.cc @@ -0,0 +1,124 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006,2007 INRIA + * + * 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 + */ +#include +#include +#include +#include "ns3/test.h" +#include "ns3/bit-serializer.h" +#include "ns3/bit-deserializer.h" + +using namespace ns3; + +/** + * \ingroup network-test + * \ingroup tests + * + * \brief Bit serialization test + */ +class BitSerializerTest : public TestCase { +public: + virtual void DoRun (void); + BitSerializerTest (); +}; + +BitSerializerTest::BitSerializerTest () + : TestCase ("BitSerializer") { +} + +void BitSerializerTest::DoRun () +{ + BitSerializer testBitSerializer1; + + testBitSerializer1.PushBits (0x55, 7); + testBitSerializer1.PushBits (0x7, 3); + testBitSerializer1.PushBits (0x0, 2); + + std::vector result = testBitSerializer1.GetBytes (); + NS_TEST_EXPECT_MSG_EQ ((result[0] == 0xab) && (result[1] == 0xc0), true, + "Incorrect serialization " << result[0] << result[1] << " instead of " << int(0xab) << " " << int(0xc0)); + + BitSerializer testBitSerializer2; + + testBitSerializer2.PushBits (0x55, 7); + testBitSerializer2.PushBits (0x7, 3); + testBitSerializer2.PushBits (0x0, 2); + + testBitSerializer2.InsertPaddingAtEnd (false); + + result = testBitSerializer2.GetBytes (); + NS_TEST_EXPECT_MSG_EQ ((result[0] == 0x0a) && (result[1] == 0xbc), true, + "Incorrect serialization " << result[0] << result[1] << " instead of " << int(0x0a) << " " << int(0xbc)); +} + +/** + * \ingroup network-test + * \ingroup tests + * + * \brief Bit deserialization test + */ +class BitDeserializerTest : public TestCase { +public: + virtual void DoRun (void); + BitDeserializerTest (); +}; + +BitDeserializerTest::BitDeserializerTest () + : TestCase ("BitDeserializer") { +} + +void BitDeserializerTest::DoRun () +{ + BitDeserializer testBitDeserializer; + uint8_t test[2]; + test[0] = 0xab; + test[1] = 0xc0; + + testBitDeserializer.PushBytes (test,2); + uint16_t nibble1 = testBitDeserializer.GetBits16 (7); + uint16_t nibble2 = testBitDeserializer.GetBits8 (3); + uint16_t nibble3 = testBitDeserializer.GetBits8 (2); + + bool result = (nibble1 == 0x55) && (nibble2 == 0x7) && (nibble3 == 0x0); + NS_TEST_EXPECT_MSG_EQ (result, true, + "Incorrect deserialization " << nibble1 << " " << nibble2 << " " << nibble3 << + " << instead of " << " " << int(0x55) << " " << int(0x7) << " " << int(0x0)); +} + +/** + * \ingroup network-test + * \ingroup tests + * + * \brief Packet Metadata TestSuite + */ +class BitSerializerTestSuite : public TestSuite +{ +public: + BitSerializerTestSuite (); +}; + + +BitSerializerTestSuite::BitSerializerTestSuite () + : TestSuite ("bit-serializer", UNIT) +{ + AddTestCase (new BitSerializerTest, TestCase::QUICK); + AddTestCase (new BitDeserializerTest, TestCase::QUICK); +} + +static BitSerializerTestSuite g_bitSerializerTest; //!< Static variable for test initialization diff --git a/src/network/utils/bit-deserializer.cc b/src/network/utils/bit-deserializer.cc new file mode 100644 --- /dev/null +++ b/src/network/utils/bit-deserializer.cc @@ -0,0 +1,143 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#include +#include "bit-deserializer.h" +#include "ns3/log.h" +#include "ns3/assert.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("BitDeserializer"); + +BitDeserializer::BitDeserializer () +{ + m_deserializing = false; +} + +void BitDeserializer::PushBytes (std::vector bytes) +{ + NS_ASSERT_MSG (!m_deserializing, "Can't add bytes after deserialization started"); + m_bytesBlob.insert (m_bytesBlob.end (), bytes.begin (), bytes.end ()); +} + +void BitDeserializer::PushBytes (uint8_t* bytes, uint32_t size) +{ + NS_ASSERT_MSG (!m_deserializing, "Can't add bytes after deserialization started"); + for (uint32_t index = 0; index < size; index++) + { + m_bytesBlob.push_back (bytes[index]); + } +} + +void BitDeserializer::PushByte (uint8_t byte) +{ + NS_ASSERT_MSG (!m_deserializing, "Can't add bytes after deserialization started"); + m_bytesBlob.push_back (byte); +} + +uint8_t BitDeserializer::GetBits8 (uint8_t size) +{ + uint8_t result = 0; + PrepareDeserialization (); + + NS_ASSERT_MSG (size <= 8, "Number of requested bits exceeds 8"); + NS_ASSERT_MSG (size <= m_blob.size (), "Number of requested bits exceeds blob size"); + + for (uint8_t i = 0; i < size; i++) + { + result <<= 1; + result |= m_blob.front (); + m_blob.pop_front (); + } + return result; +} + +uint16_t BitDeserializer::GetBits16 (uint8_t size) +{ + uint8_t result = 0; + PrepareDeserialization (); + + NS_ASSERT_MSG (size <= 16, "Number of requested bits exceeds 16"); + NS_ASSERT_MSG (size <= m_blob.size (), "Number of requested bits exceeds blob size"); + + for (uint8_t i = 0; i < size; i++) + { + result <<= 1; + result |= m_blob.front (); + m_blob.pop_front (); + } + return result; +} + +uint32_t BitDeserializer::GetBits32 (uint8_t size) +{ + uint8_t result = 0; + PrepareDeserialization (); + + NS_ASSERT_MSG (size <= 32, "Number of requested bits exceeds 32"); + NS_ASSERT_MSG (size <= m_blob.size (), "Number of requested bits exceeds blob size"); + + for (uint8_t i = 0; i < size; i++) + { + result <<= 1; + result |= m_blob.front (); + m_blob.pop_front (); + } + return result; +} + +uint64_t BitDeserializer::GetBits64 (uint8_t size) +{ + uint8_t result = 0; + PrepareDeserialization (); + + NS_ASSERT_MSG (size <= 64, "Number of requested bits exceeds 64"); + NS_ASSERT_MSG (size <= m_blob.size (), "Number of requested bits exceeds blob size"); + + for (uint8_t i = 0; i < size; i++) + { + result <<= 1; + result |= m_blob.front (); + m_blob.pop_front (); + } + return result; +} + +void BitDeserializer::PrepareDeserialization () +{ + if (m_deserializing == false) + { + m_deserializing = true; + for (auto index = m_bytesBlob.begin (); index != m_bytesBlob.end (); index++) + { + m_blob.push_back (*index & 0x80); + m_blob.push_back (*index & 0x40); + m_blob.push_back (*index & 0x20); + m_blob.push_back (*index & 0x10); + m_blob.push_back (*index & 0x8); + m_blob.push_back (*index & 0x4); + m_blob.push_back (*index & 0x2); + m_blob.push_back (*index & 0x1); + } + } +} + +} // namespace ns3 diff --git a/src/network/utils/bit-deserializer.h b/src/network/utils/bit-deserializer.h new file mode 100644 --- /dev/null +++ b/src/network/utils/bit-deserializer.h @@ -0,0 +1,103 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#ifndef BITDESERIALIZER_H_ +#define BITDESERIALIZER_H_ + +#include +#include + +namespace ns3 { + +/** + * \ingroup packet + * + * \brief Bit deserializer. See also \sa ns3::BitSerializer + * + * This class helps converting a variable number, variable sized + * number of bit-boundary fields stored as byte array representation + * and extract the original bit-fields. + * + * Note that once the Deserialization starts, it's not anymore + * possible to add more data to the `packet' to deserialize. + */ + +class BitDeserializer +{ +public: + BitDeserializer (); + + /** + * Pushes some bytes into the blob to be deserialized. + * \param bytes The bytes to add. + */ + void PushBytes (std::vector bytes); + + /** + * Pushes some bytes into the blob to be deserialized. + * \param bytes The bytes to add. + * \param size The length of the array. + */ + void PushBytes (uint8_t* bytes, uint32_t size); + + /** + * Pushes one byte into the blob to be deserialized. + * \param byte The byte to add. + */ + void PushByte (uint8_t byte); + + /** + * Pops a given number of bits from the blob front. + * \param size The number of bits to pop. + */ + uint8_t GetBits8 (uint8_t size); + + /** + * Pops a given number of bits from the blob front. + * \param size The number of bits to pop. + */ + uint16_t GetBits16 (uint8_t size); + + /** + * Pops a given number of bits from the blob front. + * \param size The number of bits to pop. + */ + uint32_t GetBits32 (uint8_t size); + + /** + * Pops a given number of bits from the blob front. + * \param size The number of bits to pop. + */ + uint64_t GetBits64 (uint8_t size); + +private: + /** + * Prepare the byte array to the deserialization. + */ + void PrepareDeserialization (); + + std::deque m_blob; //!< Blob of bits ready to be deserialized. + std::vector m_bytesBlob; //!< Blob of bytes to be deserialized. + bool m_deserializing; //!< True if the deserialization did start alredy. +}; + +} // namespace ns3 + +#endif /* BITDESERIALIZER_H_ */ diff --git a/src/network/utils/bit-serializer.cc b/src/network/utils/bit-serializer.cc new file mode 100644 --- /dev/null +++ b/src/network/utils/bit-serializer.cc @@ -0,0 +1,119 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#include +#include "bit-serializer.h" +#include "ns3/log.h" +#include "ns3/assert.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("BitSerializer"); + +BitSerializer::BitSerializer () +{ + m_padAtEnd = true; +} + +void BitSerializer::InsertPaddingAtEnd (bool padAtEnd) +{ + m_padAtEnd = padAtEnd; +} + +void BitSerializer::PadAtStart () +{ + uint8_t padding = 8 - (m_blob.size () % 8); + + m_blob.insert (m_blob.begin (), padding, false); +} + +void BitSerializer::PadAtEnd () +{ + uint8_t padding = 8 - (m_blob.size () % 8); + + m_blob.insert (m_blob.end (), padding, false); +} + +void BitSerializer::PushBits (uint64_t value, uint8_t significantBits) +{ + uint64_t mask = 1; + mask <<= significantBits - 1; + + for (uint8_t i = 0; i < significantBits; i++) + { + if (value & mask) + { + m_blob.push_back (true); + } + else + { + m_blob.push_back (false); + } + mask >>= 1; + } +} + +std::vector BitSerializer::GetBytes () +{ + std::vector result; + + m_padAtEnd ? PadAtEnd () : PadAtStart (); + + for (auto it = m_blob.begin (); it != m_blob.end ();) + { + uint8_t tmp = 0; + for (uint8_t i = 0; i < 8; ++i) + { + tmp <<= 1; + tmp |= (*it & 1); + it++; + } + result.push_back (tmp); + } + m_blob.clear (); + return result; +} + +uint8_t BitSerializer::GetBytes (uint8_t *buffer, uint32_t size) +{ + uint8_t resultLen = 0; + + m_padAtEnd ? PadAtEnd () : PadAtStart (); + + NS_ASSERT_MSG (m_blob.size () <= 8 * size, "Target buffer is too short, " << m_blob.size () / 8 << " bytes needed" ); + + for (auto it = m_blob.begin (); it != m_blob.end ();) + { + uint8_t tmp = 0; + for (uint8_t i = 0; i < 8; ++i) + { + tmp <<= 1; + tmp |= (*it & 1); + it++; + } + buffer[resultLen] = tmp; + resultLen++; + + } + m_blob.clear (); + return resultLen; +} + +} // namespace ns3 diff --git a/src/network/utils/bit-serializer.h b/src/network/utils/bit-serializer.h new file mode 100644 --- /dev/null +++ b/src/network/utils/bit-serializer.h @@ -0,0 +1,116 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#ifndef BITSERIALIZER_H_ +#define BITSERIALIZER_H_ + +#include + +namespace ns3 { + +/** + * \ingroup packet + * + * \brief Bit serializer. See also \sa ns3::BitDeserializer + * + * This class helps converting a variable number, variable sized + * number of bit-boundary fields to its final byte array representation. + * + * The typical use-case is: + * \verbatim + * aaa: A field + * bbb: B field + * ccc: C field + * ddd: D field + * 000: padding + * + * |aaaa abbb bbcc cdd0| + * \endverbatim + * + * Padding can be automatically added at the end or at the start + * of the `packet' to reach a multiple of 8 bits. By default the + * padding is added at the end of the `packet'. + * + * This class should be used in two cases: + * - When the number of fields is large. + * - When the number of fields is variable. + * In the other cases it's more convenient to use a simple bitmask. + */ + +class BitSerializer +{ +public: + BitSerializer (); + + /** + * Toggles the padding insertion policy. + * \param padAtEnd true if the padding have to be inserted at the end of the bit blob. + */ + void InsertPaddingAtEnd (bool padAtEnd); + + /** + * Pushes a number of bits in the blob. + * \param value the bits to be inserted. + * \param significantBits Number of bits to insert. + */ + void PushBits (uint64_t value, uint8_t significantBits); + + /** + * Get the bytes representation of the blob. + * Not that this operation \b automatically add the + * needed padding at the end (or start) of the blob. + * + * \warning {This operation clears the stored data.} + * + * \returns The byte representation of the blob. + */ + std::vector GetBytes (); + + /** + * Get the bytes representation of the blob. + * Not that this operation \b automatically add the + * needed padding at the end (or start) of the blob. + * + * \warning {This operation clears the stored data.} + * + * \param [out] buffer The buffer where to store the return data. + * \param [in] size The size of the buffer. + * \returns The number of bytes actually used in the buffer. + */ + uint8_t GetBytes (uint8_t *buffer, uint32_t size); + +private: + /** + * Add the padding at the start of the blob. + */ + void PadAtStart (); + + /** + * Add the padding at the end of the blob. + */ + void PadAtEnd (); + + std::vector m_blob; //!< Blob of serialized bits. + bool m_padAtEnd; //!< True if the padding must be added at the end of the blob. +}; + +} // namespace ns3 + +#endif /* BITSERIALIZER_H_ */ diff --git a/src/network/wscript b/src/network/wscript --- a/src/network/wscript +++ b/src/network/wscript @@ -25,6 +25,8 @@ 'model/trailer.cc', 'utils/address-utils.cc', 'utils/ascii-file.cc', + 'utils/bit-deserializer.cc', + 'utils/bit-serializer.cc', 'utils/crc32.cc', 'utils/data-rate.cc', 'utils/drop-tail-queue.cc', @@ -75,6 +77,7 @@ network_test = bld.create_ns3_module_test_library('network') network_test.source = [ + 'test/bit-serializer-test.cc', 'test/buffer-test.cc', 'test/drop-tail-queue-test-suite.cc', 'test/error-model-test-suite.cc', @@ -113,6 +116,8 @@ 'utils/address-utils.h', 'utils/ascii-file.h', 'utils/ascii-test.h', + 'utils/bit-deserializer.h', + 'utils/bit-serializer.h', 'utils/crc32.h', 'utils/data-rate.h', 'utils/drop-tail-queue.h',