encoding: support floating point numbers in TLV-VALUE

refs #4612

Change-Id: I3ca5970b1559c6690826045a0955a26b93663f50
diff --git a/src/encoding/block-helpers.cpp b/src/encoding/block-helpers.cpp
index 93df8e0..49fec54 100644
--- a/src/encoding/block-helpers.cpp
+++ b/src/encoding/block-helpers.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -59,7 +59,7 @@
 uint64_t
 readNonNegativeInteger(const Block& block)
 {
-  Buffer::const_iterator begin = block.value_begin();
+  auto begin = block.value_begin();
   return tlv::readNonNegativeInteger(block.value_size(), begin, block.value_end());
 }
 
@@ -120,6 +120,53 @@
   return std::string(reinterpret_cast<const char*>(block.value()), block.value_size());
 }
 
+// ---- double ----
+
+static_assert(std::numeric_limits<double>::is_iec559, "This code requires IEEE-754 doubles");
+
+template<Tag TAG>
+size_t
+prependDoubleBlock(EncodingImpl<TAG>& encoder, uint32_t type, double value)
+{
+  uint64_t temp = 0;
+  std::memcpy(&temp, &value, 8);
+  boost::endian::native_to_big_inplace(temp);
+  return encoder.prependByteArrayBlock(type, reinterpret_cast<const uint8_t*>(&temp), 8);
+}
+
+template size_t
+prependDoubleBlock<EstimatorTag>(EncodingImpl<EstimatorTag>&, uint32_t, double);
+
+template size_t
+prependDoubleBlock<EncoderTag>(EncodingImpl<EncoderTag>&, uint32_t, double);
+
+Block
+makeDoubleBlock(uint32_t type, double value)
+{
+  EncodingEstimator estimator;
+  size_t totalLength = prependDoubleBlock(estimator, type, value);
+
+  EncodingBuffer encoder(totalLength, 0);
+  prependDoubleBlock(encoder, type, value);
+
+  return encoder.block();
+}
+
+double
+readDouble(const Block& block)
+{
+  if (block.value_size() != 8) {
+    BOOST_THROW_EXCEPTION(tlv::Error("Invalid length for double (must be 8)"));
+  }
+
+  uint64_t temp = 0;
+  std::memcpy(&temp, block.value(), 8);
+  boost::endian::big_to_native_inplace(temp);
+  double d = 0;
+  std::memcpy(&d, &temp, 8);
+  return d;
+}
+
 // ---- binary ----
 
 Block
diff --git a/src/encoding/block-helpers.hpp b/src/encoding/block-helpers.hpp
index acde49c..3c2d1e1 100644
--- a/src/encoding/block-helpers.hpp
+++ b/src/encoding/block-helpers.hpp
@@ -29,7 +29,7 @@
 namespace ndn {
 namespace encoding {
 
-/** @brief Prepend a TLV element containing a non-negative integer
+/** @brief Prepend a TLV element containing a non-negative integer.
  *  @param encoder an EncodingBuffer or EncodingEstimator
  *  @param type TLV-TYPE number
  *  @param value non-negative integer value
@@ -45,7 +45,7 @@
 extern template size_t
 prependNonNegativeIntegerBlock<EncoderTag>(EncodingImpl<EncoderTag>&, uint32_t, uint64_t);
 
-/** @brief Create a TLV block containing a non-negative integer
+/** @brief Create a TLV block containing a non-negative integer.
  *  @param type TLV-TYPE number
  *  @param value non-negative integer value
  *  @sa prependNonNegativeIntegerBlock, readNonNegativeInteger
@@ -53,7 +53,7 @@
 Block
 makeNonNegativeIntegerBlock(uint32_t type, uint64_t value);
 
-/** @brief Read a non-negative integer from a TLV element
+/** @brief Read a non-negative integer from a TLV element.
  *  @param block the TLV element
  *  @throw tlv::Error block does not contain a non-negative integer
  *  @sa prependNonNegativeIntegerBlock, makeNonNegativeIntegerBlock
@@ -61,7 +61,7 @@
 uint64_t
 readNonNegativeInteger(const Block& block);
 
-/** @brief Read a non-negative integer from a TLV element and cast to the specified type
+/** @brief Read a non-negative integer from a TLV element and cast to the specified type.
  *  @tparam R result type, must be an integral type
  *  @param block the TLV element
  *  @throw tlv::Error block does not contain a valid non-negative integer or the number cannot be
@@ -79,7 +79,7 @@
   return static_cast<R>(value);
 }
 
-/** @brief Read a non-negative integer from a TLV element and cast to the specified type
+/** @brief Read a non-negative integer from a TLV element and cast to the specified type.
  *  @tparam R result type, must be an enumeration type
  *  @param block the TLV element
  *  @throw tlv::Error block does not contain a valid non-negative integer or the number cannot be
@@ -94,7 +94,7 @@
   return static_cast<R>(readNonNegativeIntegerAs<std::underlying_type_t<R>>(block));
 }
 
-/** @brief Prepend an empty TLV element
+/** @brief Prepend an empty TLV element.
  *  @param encoder an EncodingBuffer or EncodingEstimator
  *  @param type TLV-TYPE number
  *  @details The TLV element has zero-length TLV-VALUE.
@@ -110,7 +110,7 @@
 extern template size_t
 prependEmptyBlock<EncoderTag>(EncodingImpl<EncoderTag>&, uint32_t);
 
-/** @brief Create an empty TLV block
+/** @brief Create an empty TLV block.
  *  @param type TLV-TYPE number
  *  @return A TLV block with zero-length TLV-VALUE
  *  @sa prependEmptyBlock
@@ -150,6 +150,38 @@
 std::string
 readString(const Block& block);
 
+/** @brief Prepend a TLV element containing an IEEE 754 double-precision floating-point number.
+ *  @param encoder an EncodingBuffer or EncodingEstimator
+ *  @param type TLV-TYPE number
+ *  @param value a floating point number value
+ *  @sa makeDoubleBlock, readDouble
+ */
+template<Tag TAG>
+size_t
+prependDoubleBlock(EncodingImpl<TAG>& encoder, uint32_t type, double value);
+
+extern template size_t
+prependDoubleBlock<EstimatorTag>(EncodingImpl<EstimatorTag>&, uint32_t, double);
+
+extern template size_t
+prependDoubleBlock<EncoderTag>(EncodingImpl<EncoderTag>&, uint32_t, double);
+
+/** @brief Create a TLV element containing an IEEE 754 double-precision floating-point number.
+ *  @param type TLV-TYPE number
+ *  @param value floating point number value
+ *  @sa prependDoubleBlock, readDouble
+ */
+Block
+makeDoubleBlock(uint32_t type, double value);
+
+/** @brief Read TLV-VALUE of a TLV element as an IEEE 754 double-precision floating-point number.
+ *  @param block the TLV element
+ *  @return a floating point number value
+ *  @sa prependDoubleBlock, makeDoubleBlock
+ */
+double
+readDouble(const Block& block);
+
 /** @brief Create a TLV block copying TLV-VALUE from raw buffer.
  *  @param type TLV-TYPE number
  *  @param value raw buffer as TLV-VALUE
@@ -170,7 +202,7 @@
 
 namespace detail {
 
-/** @brief Create a binary block copying from RandomAccessIterator
+/** @brief Create a binary block copying from RandomAccessIterator.
  */
 template<class Iterator>
 class BinaryBlockFast
@@ -196,7 +228,7 @@
   }
 };
 
-/** @brief Create a binary block copying from generic InputIterator
+/** @brief Create a binary block copying from generic InputIterator.
  */
 template<class Iterator>
 class BinaryBlockSlow
diff --git a/tests/unit-tests/encoding/block-helpers.t.cpp b/tests/unit-tests/encoding/block-helpers.t.cpp
index 9247e0a..5b9711d 100644
--- a/tests/unit-tests/encoding/block-helpers.t.cpp
+++ b/tests/unit-tests/encoding/block-helpers.t.cpp
@@ -84,6 +84,23 @@
   BOOST_CHECK_EQUAL(readString(b), "Hello, world!");
 }
 
+BOOST_AUTO_TEST_CASE(Double)
+{
+  const double f = 0.25;
+  Block b = makeDoubleBlock(100, f);
+  BOOST_CHECK_EQUAL(b, "64083FD0000000000000"_block);
+
+  EncodingEstimator estimator;
+  size_t totalLength = prependDoubleBlock(estimator, 100, f);
+  EncodingBuffer encoder(totalLength, 0);
+  prependDoubleBlock(encoder, 100, f);
+  BOOST_CHECK_EQUAL(encoder.block(), b);
+
+  BOOST_CHECK_EQUAL(readDouble(b), f);
+  BOOST_CHECK_THROW(readDouble("4200"_block), tlv::Error);
+  BOOST_CHECK_THROW(readDouble("64043E800000"_block), tlv::Error);
+}
+
 BOOST_AUTO_TEST_CASE(Data)
 {
   std::string buf1{1, 1, 1, 1};
@@ -99,8 +116,7 @@
   BOOST_CHECK_EQUAL(b1, b3);
   BOOST_CHECK_EQUAL(b1.type(), 100);
   BOOST_CHECK_EQUAL(b1.value_size(), buf1.size());
-  BOOST_CHECK_EQUAL_COLLECTIONS(b1.value_begin(), b1.value_end(),
-                                buf2, buf2 + sizeof(buf2));
+  BOOST_CHECK_EQUAL_COLLECTIONS(b1.value_begin(), b1.value_end(), buf2, buf2 + sizeof(buf2));
 }
 
 BOOST_AUTO_TEST_CASE(Nested)