encoding: avoid misaligned memory accesses in TLV decoding
ndn::tlv::readVarNumber and ndn::tlv::readNonNegativeInteger
now properly support InputIterator, and have efficient
implementations for ContiguousIterator that do not rely on
unsafe misaligned memory accesses.
refs #4172, #4097
Change-Id: Ib02b8d908ad5cfec474679b5b3b5ccd95ba07549
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index 876fcb5..4e9c009 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -25,6 +25,7 @@
#include "buffer.hpp"
#include "endian.hpp"
+#include <cstring>
#include <iostream>
#include <iterator>
@@ -138,7 +139,7 @@
/**
* @brief Read VAR-NUMBER in NDN-TLV encoding
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
*
* @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
* the read VAR-NUMBER
@@ -148,13 +149,13 @@
* @return true if number was successfully read from input, false otherwise
* @note This call never throws exceptions
*/
-template<class InputIterator>
+template<typename Iterator>
bool
-readVarNumber(InputIterator& begin, const InputIterator& end, uint64_t& number);
+readVarNumber(Iterator& begin, const Iterator& end, uint64_t& number);
/**
* @brief Read TLV-TYPE
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
*
* @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
* the read TLV-TYPE
@@ -166,13 +167,13 @@
* @note This call is largely equivalent to tlv::readVarNumber, but it will return false if type
* is larger than 2^32-1 (TLV-TYPE in this library is implemented as uint32_t)
*/
-template<class InputIterator>
+template<typename Iterator>
bool
-readType(InputIterator& begin, const InputIterator& end, uint32_t& type);
+readType(Iterator& begin, const Iterator& end, uint32_t& type);
/**
* @brief Read VAR-NUMBER in NDN-TLV encoding
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
*
* @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
* the read VAR-NUMBER
@@ -180,13 +181,13 @@
*
* @throw tlv::Error VAR-NUMBER cannot be read
*/
-template<class InputIterator>
+template<typename Iterator>
uint64_t
-readVarNumber(InputIterator& begin, const InputIterator& end);
+readVarNumber(Iterator& begin, const Iterator& end);
/**
* @brief Read TLV Type
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
*
* @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
* the read TLV-TYPE
@@ -196,26 +197,26 @@
* @note This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
* is larger than 2^32-1 (TLV-TYPE in this library is implemented as uint32_t)
*/
-template<class InputIterator>
+template<typename Iterator>
uint32_t
-readType(InputIterator& begin, const InputIterator& end);
+readType(Iterator& begin, const Iterator& end);
/**
* @brief Get number of bytes necessary to hold value of VAR-NUMBER
*/
constexpr size_t
-sizeOfVarNumber(uint64_t varNumber);
+sizeOfVarNumber(uint64_t number);
/**
* @brief Write VAR-NUMBER to the specified stream
* @return length of written VAR-NUMBER
*/
size_t
-writeVarNumber(std::ostream& os, uint64_t varNumber);
+writeVarNumber(std::ostream& os, uint64_t number);
/**
* @brief Read nonNegativeInteger in NDN-TLV encoding
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
*
* @param [in] size size of the nonNegativeInteger
* @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
@@ -226,9 +227,9 @@
* @note How many bytes to read is directly controlled by \p size, which can be either 1, 2, 4, or 8.
* If \p size differs from \p std::distance(begin, end), tlv::Error exception will be thrown.
*/
-template<class InputIterator>
+template<typename Iterator>
uint64_t
-readNonNegativeInteger(size_t size, InputIterator& begin, const InputIterator& end);
+readNonNegativeInteger(size_t size, Iterator& begin, const Iterator& end);
/**
* @brief Get number of bytes necessary to hold value of nonNegativeInteger
@@ -253,9 +254,106 @@
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
-template<class InputIterator>
+namespace detail {
+
+/** @brief Function object to read a number from InputIterator
+ */
+template<typename Iterator>
+class ReadNumberSlow
+{
+public:
+ bool
+ operator()(size_t size, Iterator& begin, const Iterator& end, uint64_t& number) const
+ {
+ number = 0;
+ size_t count = 0;
+ for (; begin != end && count < size; ++begin, ++count) {
+ number = (number << 8) | *begin;
+ }
+ return count == size;
+ }
+};
+
+/** @brief Function object to read a number from ContiguousIterator
+ */
+template<typename Iterator>
+class ReadNumberFast
+{
+public:
+ bool
+ operator()(size_t size, Iterator& begin, const Iterator& end, uint64_t& number) const
+ {
+ if (begin + size > end) {
+ return false;
+ }
+
+ switch (size) {
+ case 1: {
+ number = *begin;
+ ++begin;
+ return true;
+ }
+ case 2: {
+ uint16_t value = 0;
+ std::memcpy(&value, &*begin, 2);
+ begin += 2;
+ number = be16toh(value);
+ return true;
+ }
+ case 4: {
+ uint32_t value = 0;
+ std::memcpy(&value, &*begin, 4);
+ begin += 4;
+ number = be32toh(value);
+ return true;
+ }
+ case 8: {
+ uint64_t value = 0;
+ std::memcpy(&value, &*begin, 8);
+ begin += 8;
+ number = be64toh(value);
+ return true;
+ }
+ default: {
+ BOOST_ASSERT(false);
+ return false;
+ }
+ }
+ }
+};
+
+/** @brief Determine whether to select ReadNumber implementation for ContiguousIterator
+ *
+ * This is not a full ContiguousIterator detection implementation. It returns true for the most
+ * common ContiguousIterator types used with TLV decoding function templates.
+ */
+template<typename Iterator,
+ typename DecayedIterator = typename std::decay<Iterator>::type,
+ typename ValueType = typename std::iterator_traits<DecayedIterator>::value_type>
+constexpr bool
+shouldSelectContiguousReadNumber()
+{
+ return (std::is_convertible<DecayedIterator, const ValueType*>::value ||
+ std::is_convertible<DecayedIterator, typename std::basic_string<ValueType>::const_iterator>::value ||
+ std::is_convertible<DecayedIterator, typename std::vector<ValueType>::const_iterator>::value) &&
+ (std::is_same<ValueType, uint8_t>::value ||
+ std::is_same<ValueType, int8_t>::value ||
+ std::is_same<ValueType, char>::value ||
+ std::is_same<ValueType, unsigned char>::value ||
+ std::is_same<ValueType, signed char>::value);
+}
+
+template<typename Iterator>
+class ReadNumber : public std::conditional<shouldSelectContiguousReadNumber<Iterator>(),
+ ReadNumberFast<Iterator>, ReadNumberSlow<Iterator>>::type
+{
+};
+
+} // namespace detail
+
+template<typename Iterator>
bool
-readVarNumber(InputIterator& begin, const InputIterator& end, uint64_t& number)
+readVarNumber(Iterator& begin, const Iterator& end, uint64_t& number)
{
if (begin == end)
return false;
@@ -264,39 +362,17 @@
++begin;
if (firstOctet < 253) {
number = firstOctet;
- }
- else if (firstOctet == 253) {
- if (end - begin < 2)
- return false;
-
- uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin);
- begin += 2;
- number = be16toh(value);
- }
- else if (firstOctet == 254) {
- if (end - begin < 4)
- return false;
-
- uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin);
- begin += 4;
- number = be32toh(value);
- }
- else { // if (firstOctet == 255)
- if (end - begin < 8)
- return false;
-
- uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
- begin += 8;
-
- number = be64toh(value);
+ return true;
}
- return true;
+ size_t size = firstOctet == 253 ? 2 :
+ firstOctet == 254 ? 4 : 8;
+ return detail::ReadNumber<Iterator>()(size, begin, end, number);
}
-template<class InputIterator>
+template<typename Iterator>
bool
-readType(InputIterator& begin, const InputIterator& end, uint32_t& type)
+readType(Iterator& begin, const Iterator& end, uint32_t& type)
{
uint64_t number = 0;
bool isOk = readVarNumber(begin, end, number);
@@ -308,51 +384,25 @@
return true;
}
-template<class InputIterator>
+template<typename Iterator>
uint64_t
-readVarNumber(InputIterator& begin, const InputIterator& end)
+readVarNumber(Iterator& begin, const Iterator& end)
{
if (begin == end)
BOOST_THROW_EXCEPTION(Error("Empty buffer during TLV processing"));
- uint64_t value;
+ uint64_t value = 0;
bool isOk = readVarNumber(begin, end, value);
- if (!isOk)
+ if (!isOk) {
BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
+ }
return value;
}
-template<>
-inline bool
-readVarNumber<std::istream_iterator<uint8_t>>(std::istream_iterator<uint8_t>& begin,
- const std::istream_iterator<uint8_t>& end,
- uint64_t& value)
-{
- if (begin == end)
- return false;
-
- uint8_t firstOctet = *begin;
- ++begin;
- if (firstOctet < 253) {
- value = firstOctet;
- return true;
- }
-
- size_t expectedSize = firstOctet == 253 ? 2 :
- firstOctet == 254 ? 4 : 8;
- value = 0;
- size_t count = 0;
- for (; begin != end && count < expectedSize; ++count) {
- value = (value << 8) | *begin;
- ++begin;
- }
- return count == expectedSize;
-}
-
-template<class InputIterator>
+template<typename Iterator>
uint32_t
-readType(InputIterator& begin, const InputIterator& end)
+readType(Iterator& begin, const Iterator& end)
{
uint64_t type = readVarNumber(begin, end);
if (type > std::numeric_limits<uint32_t>::max()) {
@@ -363,105 +413,56 @@
}
constexpr size_t
-sizeOfVarNumber(uint64_t varNumber)
+sizeOfVarNumber(uint64_t number)
{
- return varNumber < 253 ? 1 :
- varNumber <= std::numeric_limits<uint16_t>::max() ? 3 :
- varNumber <= std::numeric_limits<uint32_t>::max() ? 5 : 9;
+ return number < 253 ? 1 :
+ number <= std::numeric_limits<uint16_t>::max() ? 3 :
+ number <= std::numeric_limits<uint32_t>::max() ? 5 : 9;
}
inline size_t
-writeVarNumber(std::ostream& os, uint64_t varNumber)
+writeVarNumber(std::ostream& os, uint64_t number)
{
- if (varNumber < 253) {
- os.put(static_cast<char>(varNumber));
+ if (number < 253) {
+ os.put(static_cast<char>(number));
return 1;
}
- else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+ else if (number <= std::numeric_limits<uint16_t>::max()) {
os.put(static_cast<char>(253));
- uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
+ uint16_t value = htobe16(static_cast<uint16_t>(number));
os.write(reinterpret_cast<const char*>(&value), 2);
return 3;
}
- else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+ else if (number <= std::numeric_limits<uint32_t>::max()) {
os.put(static_cast<char>(254));
- uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
+ uint32_t value = htobe32(static_cast<uint32_t>(number));
os.write(reinterpret_cast<const char*>(&value), 4);
return 5;
}
else {
os.put(static_cast<char>(255));
- uint64_t value = htobe64(varNumber);
+ uint64_t value = htobe64(number);
os.write(reinterpret_cast<const char*>(&value), 8);
return 9;
}
}
-template<class InputIterator>
+template<typename Iterator>
uint64_t
-readNonNegativeInteger(size_t size, InputIterator& begin, const InputIterator& end)
-{
- switch (size) {
- case 1: {
- if (end - begin < 1)
- BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
- uint8_t value = *begin;
- begin++;
- return value;
- }
- case 2: {
- if (end - begin < 2)
- BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
- uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin);
- begin += 2;
- return be16toh(value);
- }
- case 4: {
- if (end - begin < 4)
- BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
- uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin);
- begin += 4;
- return be32toh(value);
- }
- case 8: {
- if (end - begin < 8)
- BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
- uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
- begin += 8;
- return be64toh(value);
- }
- }
- BOOST_THROW_EXCEPTION(Error("Invalid length for nonNegativeInteger "
- "(only 1, 2, 4, and 8 are allowed)"));
-}
-
-template<>
-inline uint64_t
-readNonNegativeInteger<std::istream_iterator<uint8_t>>(size_t size,
- std::istream_iterator<uint8_t>& begin,
- const std::istream_iterator<uint8_t>& end)
+readNonNegativeInteger(size_t size, Iterator& begin, const Iterator& end)
{
if (size != 1 && size != 2 && size != 4 && size != 8) {
BOOST_THROW_EXCEPTION(Error("Invalid length for nonNegativeInteger "
"(only 1, 2, 4, and 8 are allowed)"));
}
- uint64_t value = 0;
- size_t count = 0;
- for (; begin != end && count < size; ++count) {
- value = (value << 8) | *begin;
- begin++;
- }
-
- if (count != size) {
+ uint64_t number = 0;
+ bool isOk = detail::ReadNumber<Iterator>()(size, begin, end, number);
+ if (!isOk) {
BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
}
- return value;
+ return number;
}
constexpr size_t
diff --git a/tests/unit-tests/encoding/tlv.t.cpp b/tests/unit-tests/encoding/tlv.t.cpp
index 28f007b..edbb98f 100644
--- a/tests/unit-tests/encoding/tlv.t.cpp
+++ b/tests/unit-tests/encoding/tlv.t.cpp
@@ -23,6 +23,7 @@
#include "boost-test.hpp"
+#include <boost/concept_archetype.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/lexical_cast.hpp>
@@ -35,16 +36,67 @@
BOOST_AUTO_TEST_SUITE(TestTlv)
using ArrayStream = boost::iostreams::stream<boost::iostreams::array_source>;
-using Iterator = std::istream_iterator<uint8_t>;
+using StreamIterator = std::istream_iterator<uint8_t>;
+
+#define ASSERT_READ_NUMBER_IS_FAST(T) \
+ static_assert(std::is_base_of<detail::ReadNumberFast<T>, detail::ReadNumber<T>>::value, \
+ # T " should use ReadNumberFast")
+#define ASSERT_READ_NUMBER_IS_SLOW(T) \
+ static_assert(std::is_base_of<detail::ReadNumberSlow<T>, detail::ReadNumber<T>>::value, \
+ # T " should use ReadNumberSlow")
+
+ASSERT_READ_NUMBER_IS_FAST(const uint8_t*);
+ASSERT_READ_NUMBER_IS_FAST(uint8_t*);
+ASSERT_READ_NUMBER_IS_FAST(int8_t*);
+ASSERT_READ_NUMBER_IS_FAST(char*);
+ASSERT_READ_NUMBER_IS_FAST(unsigned char*);
+ASSERT_READ_NUMBER_IS_FAST(signed char*);
+ASSERT_READ_NUMBER_IS_FAST(const uint8_t[]);
+ASSERT_READ_NUMBER_IS_FAST(uint8_t[]);
+ASSERT_READ_NUMBER_IS_FAST(const uint8_t[12]);
+ASSERT_READ_NUMBER_IS_FAST(uint8_t[12]);
+using Uint8Array = std::array<uint8_t, 87>;
+ASSERT_READ_NUMBER_IS_FAST(Uint8Array::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(Uint8Array::iterator);
+using CharArray = std::array<char, 87>;
+ASSERT_READ_NUMBER_IS_FAST(CharArray::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::string::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::string::iterator);
+ASSERT_READ_NUMBER_IS_FAST(Buffer::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(Buffer::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<uint8_t>::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<uint8_t>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<int8_t>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<char>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<unsigned char>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<signed char>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<bool>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint16_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint32_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint64_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::list<uint8_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(StreamIterator);
BOOST_AUTO_TEST_SUITE(VarNumber)
+// This check ensures readVarNumber and readType only require InputIterator concept and nothing
+// more. This function should compile, but should never be executed.
+void
+checkArchetype()
+{
+ boost::input_iterator_archetype<uint8_t> begin, end;
+ uint64_t number = readVarNumber(begin, end);
+ uint32_t type = readType(begin, end);;
+ readVarNumber(begin, end, number);
+ readType(begin, end, type);
+}
+
static const uint8_t BUFFER[] = {
0x01, // == 1
0xfc, // == 252
0xfd, 0x00, 0xfd, // == 253
0xfe, 0x00, 0x01, 0x00, 0x00, // == 65536
- 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 // == 4294967296LL
+ 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 // == 4294967296
};
BOOST_AUTO_TEST_CASE(SizeOf)
@@ -53,7 +105,7 @@
BOOST_CHECK_EQUAL(sizeOfVarNumber(252), 1);
BOOST_CHECK_EQUAL(sizeOfVarNumber(253), 3);
BOOST_CHECK_EQUAL(sizeOfVarNumber(65536), 5);
- BOOST_CHECK_EQUAL(sizeOfVarNumber(4294967296LL), 9);
+ BOOST_CHECK_EQUAL(sizeOfVarNumber(4294967296), 9);
}
BOOST_AUTO_TEST_CASE(Write)
@@ -64,7 +116,7 @@
writeVarNumber(os, 252);
writeVarNumber(os, 253);
writeVarNumber(os, 65536);
- writeVarNumber(os, 4294967296LL);
+ writeVarNumber(os, 4294967296);
std::string buffer = os.str();
const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.c_str());
@@ -138,137 +190,137 @@
BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 9, value), true);
begin = BUFFER + 10;
BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 9));
- BOOST_CHECK_EQUAL(value, 4294967296LL);
+ BOOST_CHECK_EQUAL(value, 4294967296);
}
BOOST_AUTO_TEST_CASE(ReadFromStream)
{
- Iterator end; // end of stream
+ StreamIterator end; // end of stream
uint64_t value;
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
BOOST_CHECK_EQUAL(value, 1);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
BOOST_CHECK_EQUAL(value, 252);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 2);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 2);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 3);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 3);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
BOOST_CHECK_EQUAL(value, 253);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 4);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 4);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 5);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 5);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
BOOST_CHECK_EQUAL(value, 65536);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 8);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 8);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 9);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 9);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
- BOOST_CHECK_EQUAL(value, 4294967296LL);
+ BOOST_CHECK_EQUAL(value, 4294967296);
}
}
@@ -276,12 +328,21 @@
BOOST_AUTO_TEST_SUITE(NonNegativeInteger)
+// This check ensures readNonNegativeInteger only requires InputIterator concept and nothing more.
+// This function should compile, but should never be executed.
+void
+checkArchetype()
+{
+ boost::input_iterator_archetype<uint8_t> begin, end;
+ readNonNegativeInteger(0, begin, end);
+}
+
static const uint8_t BUFFER[] = {
0x01, // 1
0xff, // 255
0x01, 0x02, // 258
- 0x01, 0x01, 0x01, 0x02, // 16843010LL
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 // 72340172838076674LL
+ 0x01, 0x01, 0x01, 0x02, // 16843010
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 // 72340172838076674
};
BOOST_AUTO_TEST_CASE(SizeOf)
@@ -291,9 +352,9 @@
BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(255), 1);
BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(256), 2);
BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(65536), 4);
- BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(16843009LL), 4);
- BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(4294967296LL), 8);
- BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(72340172838076673LL), 8);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(16843009), 4);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(4294967296), 8);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(72340172838076673), 8);
}
BOOST_AUTO_TEST_CASE(Write)
@@ -303,8 +364,8 @@
writeNonNegativeInteger(os, 1);
writeNonNegativeInteger(os, 255);
writeNonNegativeInteger(os, 258);
- writeNonNegativeInteger(os, 16843010LL);
- writeNonNegativeInteger(os, 72340172838076674LL);
+ writeNonNegativeInteger(os, 16843010);
+ writeNonNegativeInteger(os, 72340172838076674);
std::string buffer = os.str();
const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.c_str());
@@ -330,11 +391,11 @@
BOOST_CHECK_EQUAL(begin, BUFFER + 4);
begin = BUFFER + 4;
- BOOST_CHECK_EQUAL(readNonNegativeInteger(4, begin, begin + 4), 16843010LL);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(4, begin, begin + 4), 16843010);
BOOST_CHECK_EQUAL(begin, BUFFER + 8);
begin = BUFFER + 8;
- BOOST_CHECK_EQUAL(readNonNegativeInteger(8, begin, begin + 8), 72340172838076674LL);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(8, begin, begin + 8), 72340172838076674);
BOOST_CHECK_EQUAL(begin, BUFFER + 16);
// invalid size
@@ -354,45 +415,45 @@
BOOST_AUTO_TEST_CASE(ReadFromStream)
{
- Iterator end; // end of stream
+ StreamIterator end; // end of stream
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), sizeof(BUFFER));
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_EQUAL(readNonNegativeInteger(1, begin, end), 1);
BOOST_CHECK_EQUAL(readNonNegativeInteger(1, begin, end), 255);
BOOST_CHECK_EQUAL(readNonNegativeInteger(2, begin, end), 258);
- BOOST_CHECK_EQUAL(readNonNegativeInteger(4, begin, end), 16843010LL);
- BOOST_CHECK_EQUAL(readNonNegativeInteger(8, begin, end), 72340172838076674LL);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(4, begin, end), 16843010);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(8, begin, end), 72340172838076674);
BOOST_CHECK(begin == end);
}
// invalid size
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 3);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readNonNegativeInteger(3, begin, end), Error);
}
// available buffer smaller than size
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 0);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readNonNegativeInteger(1, begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readNonNegativeInteger(2, begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 3);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readNonNegativeInteger(4, begin, end), Error);
}
{
ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 7);
- Iterator begin(stream);
+ StreamIterator begin(stream);
BOOST_CHECK_THROW(readNonNegativeInteger(8, begin, end), Error);
}
}