encoding: Add TLV-related definitions, data structures, abstraction, and functions
Change-Id: I2dbdd5ae17f1f60cdd65b7e46e5b43b25c7f025c
diff --git a/Makefile.am b/Makefile.am
index d14b0b8..6ed2873 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -82,6 +82,7 @@
src/encoding/binary-xml-encoder.hpp \
src/encoding/binary-xml-structure-decoder.hpp \
src/encoding/binary-xml-wire-format.cpp \
+ src/encoding/block.cpp \
src/encoding/der/der-exception.cpp \
src/encoding/der/der-exception.hpp \
src/encoding/der/der.cpp \
diff --git a/include/ndn-cpp/encoding/block-helpers.hpp b/include/ndn-cpp/encoding/block-helpers.hpp
new file mode 100644
index 0000000..c3ae196
--- /dev/null
+++ b/include/ndn-cpp/encoding/block-helpers.hpp
@@ -0,0 +1,62 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_BLOCK_HELPERS_HPP
+#define NDN_BLOCK_HELPERS_HPP
+
+#include "block.hpp"
+
+namespace ndn {
+
+inline Block
+nonNegativeIntegerBlock(uint32_t type, uint64_t value)
+{
+ OBufferStream os;
+ Tlv::writeVarNumber(os, type);
+ Tlv::writeVarNumber(os, Tlv::sizeOfNonNegativeInteger(value));
+ Tlv::writeNonNegativeInteger(os, value);
+ return Block(os.buf());
+}
+
+inline uint64_t
+readNonNegativeInteger(const Block &block)
+{
+ Buffer::const_iterator begin = block.value_begin();
+ return Tlv::readNonNegativeInteger(block.value_size(), begin, block.value_end());
+}
+
+inline Block
+booleanBlock(uint32_t type)
+{
+ OBufferStream os;
+ Tlv::writeVarNumber(os, type);
+ Tlv::writeVarNumber(os, 0);
+ return Block(os.buf());
+}
+
+inline Block
+dataBlock(uint32_t type, const char *data, size_t dataSize)
+{
+ OBufferStream os;
+ Tlv::writeVarNumber(os, type);
+ Tlv::writeVarNumber(os, dataSize);
+ os.write(data, dataSize);
+
+ return Block(os.buf());
+}
+
+inline Block
+dataBlock(uint32_t type, const unsigned char *data, size_t dataSize)
+{
+ return dataBlock(type, reinterpret_cast<const char*>(data), dataSize);
+}
+
+} // namespace ndn
+
+#endif // NDN_BLOCK_HELPERS_HPP
diff --git a/include/ndn-cpp/encoding/block.hpp b/include/ndn-cpp/encoding/block.hpp
new file mode 100644
index 0000000..6d104b1
--- /dev/null
+++ b/include/ndn-cpp/encoding/block.hpp
@@ -0,0 +1,377 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_BLOCK_HPP
+#define NDN_BLOCK_HPP
+
+#include <ndn-cpp/common.hpp>
+
+#include <list>
+#include <exception>
+
+#include "buffer.hpp"
+#include "tlv.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+
+/**
+ * @brief Class representing wire element of the NDN packet
+ */
+class Block
+{
+public:
+ typedef std::list<Block>::iterator element_iterator;
+ typedef std::list<Block>::const_iterator element_const_iterator;
+
+ /// @brief Error that can be thrown from the block
+ struct Error : public std::runtime_error { Error(const std::string &what) : std::runtime_error(what) {} };
+
+ /**
+ * @brief Default constructor to create an empty Block
+ */
+ Block();
+
+ /**
+ * @brief A helper version of a constructor to create Block from the raw buffer (type and value-length parsing)
+ */
+ Block(const ConstBufferPtr &buffer);
+
+ /**
+ * @brief A helper version of a constructor to create Block from the raw buffer (type and value-length parsing)
+ */
+ Block(const uint8_t *buffer, size_t maxlength);
+
+ Block(const void *buffer, size_t maxlength);
+
+ /**
+ * @brief Create Block from the wire buffer (no parsing)
+ *
+ * This version of the constructor does not do any parsing
+ */
+ Block(const ConstBufferPtr &wire,
+ uint32_t type,
+ const Buffer::const_iterator &begin, Buffer::const_iterator &end,
+ const Buffer::const_iterator &valueBegin, Buffer::const_iterator &valueEnd);
+
+ /**
+ * @brief Create Block of a specific type with empty wire buffer
+ */
+ Block(uint32_t type);
+
+ /**
+ * @brief Create Block of a specific type with the specified value
+ *
+ * The underlying buffer hold only value, additional operations are needed
+ * to construct wire encoding, one need to prepend the wire buffer with type
+ * and value-length VAR-NUMBERs
+ */
+ Block(uint32_t type, const ConstBufferPtr &value);
+
+ /**
+ * @brief Create nested Block of a specific type with the specified value
+ *
+ * The underlying buffer hold only value, additional operations are needed
+ * to construct wire encoding, one need to prepend the wire buffer with type
+ * and value-length VAR-NUMBERs
+ */
+ Block(uint32_t type, const Block &value);
+
+ /**
+ * @brief Check if the Block has fully encoded wire
+ */
+ inline bool
+ hasWire() const;
+
+ /**
+ * @brief Check if the Block has value block (no type and length are encoded)
+ */
+ inline bool
+ hasValue() const;
+
+ /**
+ * @brief Reset wire buffer of the element
+ */
+ inline void
+ reset();
+
+ void
+ parse();
+
+ void
+ encode();
+
+ inline uint32_t
+ type() const;
+
+ /**
+ * @brief Get the first subelement of the requested type
+ */
+ inline const Block &
+ get(uint32_t type) const;
+
+ inline Block &
+ get(uint32_t type);
+
+ inline element_iterator
+ find(uint32_t type);
+
+ inline element_const_iterator
+ find(uint32_t type) const;
+
+ inline void
+ push_back(const Block &element);
+
+ /**
+ * @brief Get all subelements
+ */
+ inline const std::list<Block>&
+ getAll () const;
+
+ inline std::list<Block>&
+ getAll ();
+
+ /**
+ * @brief Get all elements of the requested type
+ */
+ std::list<Block>
+ getAll(uint32_t type) const;
+
+ inline Buffer::const_iterator
+ begin() const;
+
+ inline Buffer::const_iterator
+ end() const;
+
+ inline size_t
+ size() const;
+
+ inline Buffer::const_iterator
+ value_begin() const;
+
+ inline Buffer::const_iterator
+ value_end() const;
+
+ inline const uint8_t*
+ wire() const;
+
+ inline const uint8_t*
+ value() const;
+
+ inline size_t
+ value_size() const;
+
+protected:
+ ConstBufferPtr m_buffer;
+
+ uint32_t m_type;
+
+ Buffer::const_iterator m_begin;
+ Buffer::const_iterator m_end;
+ uint32_t m_size;
+
+ Buffer::const_iterator m_value_begin;
+ Buffer::const_iterator m_value_end;
+
+ std::list<Block> m_subBlocks;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+inline bool
+Block::hasWire() const
+{
+ return m_buffer && (m_begin != m_end);
+}
+
+inline bool
+Block::hasValue() const
+{
+ return static_cast<bool>(m_buffer);
+}
+
+inline void
+Block::reset()
+{
+ m_buffer.reset(); // reset of the shared_ptr
+ m_subBlocks.clear(); // remove all parsed subelements
+
+ m_type = std::numeric_limits<uint32_t>::max();
+ m_begin = m_end = m_value_begin = m_value_end = Buffer::const_iterator(); // not really necessary, but for safety
+}
+
+inline uint32_t
+Block::type() const
+{
+ return m_type;
+}
+
+inline const Block &
+Block::get(uint32_t type) const
+{
+ for (element_const_iterator i = m_subBlocks.begin ();
+ i != m_subBlocks.end();
+ i++)
+ {
+ if (i->type () == type)
+ {
+ return *i;
+ }
+ }
+
+ throw Error("(Block::get) Requested a non-existed type [" + boost::lexical_cast<std::string>(type) + "] from Block");
+}
+
+inline Block &
+Block::get(uint32_t type)
+{
+ for (element_iterator i = m_subBlocks.begin ();
+ i != m_subBlocks.end();
+ i++)
+ {
+ if (i->type () == type)
+ {
+ return *i;
+ }
+ }
+
+ throw Error("(Block::get) Requested a non-existed type [" + boost::lexical_cast<std::string>(type) + "] from Block");
+}
+
+inline Block::element_const_iterator
+Block::find(uint32_t type) const
+{
+ for (element_const_iterator i = m_subBlocks.begin ();
+ i != m_subBlocks.end();
+ i++)
+ {
+ if (i->type () == type)
+ {
+ return i;
+ }
+ }
+ return m_subBlocks.end();
+}
+
+inline Block::element_iterator
+Block::find(uint32_t type)
+{
+ for (element_iterator i = m_subBlocks.begin ();
+ i != m_subBlocks.end();
+ i++)
+ {
+ if (i->type () == type)
+ {
+ return i;
+ }
+ }
+ return m_subBlocks.end();
+}
+
+inline void
+Block::push_back(const Block &element)
+{
+ m_subBlocks.push_back(element);
+}
+
+
+inline const std::list<Block>&
+Block::getAll () const
+{
+ return m_subBlocks;
+}
+
+inline std::list<Block>&
+Block::getAll ()
+{
+ return m_subBlocks;
+}
+
+
+inline Buffer::const_iterator
+Block::begin() const
+{
+ if (!hasWire())
+ throw Error("Underlying wire buffer is empty");
+
+ return m_begin;
+}
+
+inline Buffer::const_iterator
+Block::end() const
+{
+ if (!hasWire())
+ throw Error("Underlying wire buffer is empty");
+
+ return m_end;
+}
+
+inline size_t
+Block::size() const
+{
+ if (hasWire() || hasValue()) {
+ return m_size;
+ }
+ else
+ throw Error("Block size cannot be determined (undefined block size)");
+}
+
+inline Buffer::const_iterator
+Block::value_begin() const
+{
+ if (!hasValue())
+ throw Error("(Block::value_begin) Underlying value buffer is empty");
+
+ return m_value_begin;
+}
+
+inline Buffer::const_iterator
+Block::value_end() const
+{
+ if (!hasValue())
+ throw Error("(Block::value_end) Underlying value buffer is empty");
+
+ return m_value_end;
+}
+
+inline const uint8_t*
+Block::wire() const
+{
+ if (!hasWire())
+ throw Error("(Block::wire) Underlying wire buffer is empty");
+
+ return &*m_begin;
+}
+
+inline const uint8_t*
+Block::value() const
+{
+ if (!hasValue())
+ throw Error("(Block::value) Underlying value buffer is empty");
+
+ return &*m_value_begin;
+}
+
+inline size_t
+Block::value_size() const
+{
+ if (!hasValue())
+ throw Error("(Block::value_size) Underlying value buffer is empty");
+
+ return m_value_end - m_value_begin;
+}
+
+} // ndn
+
+#include "block-helpers.hpp"
+
+#endif // NDN_BLOCK_HPP
diff --git a/include/ndn-cpp/encoding/buffer.hpp b/include/ndn-cpp/encoding/buffer.hpp
new file mode 100644
index 0000000..ec71112
--- /dev/null
+++ b/include/ndn-cpp/encoding/buffer.hpp
@@ -0,0 +1,199 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_BUFFER_HPP
+#define NDN_BUFFER_HPP
+
+#include <ndn-cpp/common.hpp>
+
+#include <boost/iostreams/detail/ios.hpp>
+#include <boost/iostreams/categories.hpp>
+#include <boost/iostreams/stream.hpp>
+
+#include <vector>
+
+namespace ndn {
+
+class Buffer;
+typedef ptr_lib::shared_ptr<const Buffer> ConstBufferPtr;
+typedef ptr_lib::shared_ptr<Buffer> BufferPtr;
+
+/**
+ * @brief Class representing a general-use automatically managed/resized buffer
+ *
+ * In most respect, Buffer class is equivalent to std::vector<uint8_t> and is in fact
+ * uses it as a base class. In addition to that, it provides buf() and buf<T>() helper
+ * method for easier access to the underlying data (buf<T>() casts pointer to the requested class)
+ */
+class Buffer : public std::vector<uint8_t>
+{
+public:
+ /**
+ * @brief Creates an empty buffer
+ */
+ Buffer ()
+ {
+ }
+
+ /**
+ * @brief Create a buffer by copying the supplied data from a const buffer
+ * @param buf const pointer to buffer
+ * @param length length of the buffer to copy
+ */
+ Buffer (const void *buf, size_t length)
+ : std::vector<uint8_t> (reinterpret_cast<const uint8_t*> (buf), reinterpret_cast<const uint8_t*> (buf) + length)
+ {
+ }
+
+ /**
+ * @brief Create a buffer by copying the supplied data using iterator interface
+ *
+ * Note that the supplied iterators must be compatible with std::vector<uint8_t> interface
+ *
+ * @param first iterator to a first element to copy
+ * @param last iterator to an element immediately following the last element to copy
+ */
+ template <class InputIterator>
+ Buffer (InputIterator first, InputIterator last)
+ : std::vector<uint8_t> (first, last)
+ {
+ }
+
+ /**
+ * @brief Get pointer to the first byte of the buffer
+ */
+ inline uint8_t*
+ get ()
+ {
+ return &front ();
+ }
+
+ /**
+ * @brief Get pointer to the first byte of the buffer (alternative version)
+ */
+ inline uint8_t*
+ buf ()
+ {
+ return &front ();
+ }
+
+ /**
+ * @brief Get pointer to the first byte of the buffer and cast
+ * it (reinterpret_cast) to the requested type T
+ */
+ template<class T>
+ inline T*
+ get ()
+ {
+ return reinterpret_cast<T *>(&front ());
+ }
+
+ /**
+ * @brief Get pointer to the first byte of the buffer (alternative version)
+ */
+ inline const uint8_t*
+ buf () const
+ {
+ return &front ();
+ }
+
+ /**
+ * @brief Get const pointer to the first byte of the buffer
+ */
+ inline const uint8_t*
+ get () const
+ {
+ return &front ();
+ }
+
+ /**
+ * @brief Get const pointer to the first byte of the buffer and cast
+ * it (reinterpret_cast) to the requested type T
+ */
+ template<class T>
+ inline const T*
+ get () const
+ {
+ return reinterpret_cast<const T *>(&front ());
+ }
+};
+
+/// @cond include_hidden
+namespace iostreams
+{
+
+class buffer_append_device {
+public:
+ typedef char char_type;
+ typedef boost::iostreams::sink_tag category;
+
+ buffer_append_device (Buffer& container)
+ : m_container (container)
+ {
+ }
+
+ std::streamsize
+ write(const char_type* s, std::streamsize n)
+ {
+ std::copy (s, s+n, std::back_inserter(m_container));
+ return n;
+ }
+
+protected:
+ Buffer& m_container;
+};
+
+} // iostreams
+/// @endcond
+
+/**
+ * Class implementing interface similar to ostringstream, but to construct ndn::Buffer
+ *
+ * The benefit of using stream interface is that it provides automatic buffering of written data
+ * and eliminates (or reduces) overhead of resizing the underlying buffer when writing small pieces of data.
+ *
+ * Usage example:
+ * @code
+ * OBufferStream obuf;
+ * obuf.put(0);
+ * obuf.write(another_buffer, another_buffer_size);
+ * ptr_lib::shared_ptr<Buffer> buf = obuf.get();
+ * @endcode
+ */
+struct OBufferStream : public boost::iostreams::stream<iostreams::buffer_append_device>
+{
+ /**
+ * Default constructor
+ */
+ OBufferStream ()
+ : m_buffer (ptr_lib::make_shared<Buffer> ())
+ , m_device (*m_buffer)
+ {
+ open (m_device);
+ }
+
+ /**
+ * Flush written data to the stream and return shared pointer to the underlying buffer
+ */
+ ptr_lib::shared_ptr<Buffer>
+ buf ()
+ {
+ flush ();
+ return m_buffer;
+ }
+
+private:
+ BufferPtr m_buffer;
+ iostreams::buffer_append_device m_device;
+};
+
+
+} // ndn
+
+#endif // NDN_BUFFER_HPP
diff --git a/include/ndn-cpp/encoding/endian.h b/include/ndn-cpp/encoding/endian.h
new file mode 100644
index 0000000..9ea5c96
--- /dev/null
+++ b/include/ndn-cpp/encoding/endian.h
@@ -0,0 +1,39 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Junxiao Shi <shijunxiao@email.arizona.edu>
+ */
+
+#ifdef __linux__
+
+#include <endian.h>
+
+#endif
+
+#ifdef __FreeBSD__
+
+#include <sys/endian.h>
+
+#endif
+
+#ifdef __APPLE__
+
+#include <libkern/OSByteOrder.h>
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define be16toh(x) OSSwapBigToHostInt16(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define htole64(x) OSSwapHostToLittleInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#define le64toh(x) OSSwapLittleToHostInt64(x)
+
+#endif
+
diff --git a/include/ndn-cpp/encoding/tlv.hpp b/include/ndn-cpp/encoding/tlv.hpp
new file mode 100644
index 0000000..0edf94a
--- /dev/null
+++ b/include/ndn-cpp/encoding/tlv.hpp
@@ -0,0 +1,326 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_TLV_HPP
+#define NDN_TLV_HPP
+
+#include "buffer.hpp"
+#include "endian.h"
+
+namespace ndn {
+
+/**
+ * @brief Namespace defining NDN-TLV related constants and procedures
+ */
+namespace Tlv {
+
+struct Error : public std::runtime_error { Error(const std::string &what) : std::runtime_error(what) {} };
+
+enum {
+ Interest = 0,
+ Data = 1,
+ Name = 2,
+ NameComponent = 3,
+ Selectors = 4,
+ Nonce = 5,
+ Scope = 6,
+ InterestLifetime = 7,
+ MinSuffixComponents = 8,
+ MaxSuffixComponents = 9,
+ PublisherPublicKeyLocator = 10,
+ Exclude = 11,
+ ChildSelector = 12,
+ MustBeFresh = 13,
+ Any = 14,
+ MetaInfo = 15,
+ Content = 16,
+ SignatureInfo = 17,
+ SignatureValue = 18,
+ ContentType = 19,
+ FreshnessPeriod = 20,
+ SignatureType = 21,
+ KeyLocator = 22,
+ KeyLocatorDigest = 23,
+
+ AppPrivateBlock1 = 128,
+ AppPrivateBlock2 = 32767
+};
+
+enum SignatureType {
+ DigestSha256 = 0,
+ SignatureSha256WithRsa = 1,
+};
+
+enum ConentType {
+ ContentType_Default = 0,
+ ContentType_Link = 1,
+ ContentType_Key = 2,
+};
+
+/**
+ * @brief Read VAR-NUMBER in NDN-TLV encoding
+ *
+ * This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
+ *
+ * Note that after call finished, begin will point to the first byte after the read VAR-NUMBER
+ */
+template<class InputIterator>
+inline uint64_t
+readVarNumber(InputIterator &begin, const InputIterator &end);
+
+/**
+ * @brief Read TLV Type
+ *
+ * This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
+ * is larger than 2^32-1 (type in this library is implemented as uint32_t)
+ */
+template<class InputIterator>
+inline uint32_t
+readType(InputIterator &begin, const InputIterator &end);
+
+/**
+ * @brief Get number of bytes necessary to hold value of VAR-NUMBER
+ */
+inline size_t
+sizeOfVarNumber(uint64_t varNumber);
+
+/**
+ * @brief Write VAR-NUMBER to the specified stream
+ */
+inline size_t
+writeVarNumber(std::ostream &os, uint64_t varNumber);
+
+/**
+ * @brief Read nonNegativeInteger in NDN-TLV encoding
+ *
+ * This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
+ *
+ * Note that after call finished, begin will point to the first byte after the read VAR-NUMBER
+ *
+ * How many bytes will be read is directly controlled by the size parameter, which can be either
+ * 1, 2, 4, or 8. If the value of size is different, then an exception will be thrown.
+ */
+template<class InputIterator>
+inline uint64_t
+readNonNegativeInteger(size_t size, InputIterator &begin, const InputIterator &end);
+
+/**
+ * @brief Get number of bytes necessary to hold value of nonNegativeInteger
+ */
+inline size_t
+sizeOfNonNegativeInteger(uint64_t varNumber);
+
+/**
+ * @brief Write nonNegativeInteger to the specified stream
+ */
+inline size_t
+writeNonNegativeInteger(std::ostream &os, uint64_t varNumber);
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+// Inline implementations
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+template<class InputIterator>
+inline uint64_t
+readVarNumber(InputIterator &begin, const InputIterator &end)
+{
+ if (begin == end)
+ throw Error("Empty buffer during TLV processing");
+
+ uint8_t value = *begin;
+ ++begin;
+ if (value < 253)
+ {
+ return value;
+ }
+ else if (value == 253)
+ {
+ if (end - begin < 2)
+ throw Error("Insufficient data during TLV processing");
+
+ uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
+ begin += 2;
+ return be16toh(value);
+ }
+ else if (value == 254)
+ {
+ if (end - begin < 4)
+ throw Error("Insufficient data during TLV processing");
+
+ uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
+ begin += 4;
+ return be32toh(value);
+ }
+ else // if (value == 255)
+ {
+ if (end - begin < 8)
+ throw Error("Insufficient data during TLV processing");
+
+ uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
+ begin += 8;
+
+ return be64toh(value);
+ }
+}
+
+template<class InputIterator>
+inline uint32_t
+readType(InputIterator &begin, const InputIterator &end)
+{
+ uint64_t type = readVarNumber(begin, end);
+ if (type > std::numeric_limits<uint32_t>::max())
+ {
+ throw Error("TLV type code exceeds allowed maximum");
+ }
+
+ return static_cast<uint32_t> (type);
+}
+
+size_t
+sizeOfVarNumber(uint64_t varNumber)
+{
+ if (varNumber < 253) {
+ return 1;
+ }
+ else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+ return 3;
+ }
+ else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+ return 5;
+ }
+ else {
+ return 9;
+ }
+}
+
+inline size_t
+writeVarNumber(std::ostream &os, uint64_t varNumber)
+{
+ if (varNumber < 253) {
+ os.put(static_cast<uint8_t> (varNumber));
+ return 1;
+ }
+ else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+ os.put(253);
+ uint16_t value = htobe16(static_cast<uint16_t> (varNumber));
+ os.write(reinterpret_cast<const char*> (&value), 2);
+ return 3;
+ }
+ else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+ os.put(254);
+ uint32_t value = htobe32(static_cast<uint32_t> (varNumber));
+ os.write(reinterpret_cast<const char*> (&value), 4);
+ return 5;
+ }
+ else {
+ os.put(255);
+ uint64_t value = htobe64(varNumber);
+ os.write(reinterpret_cast<const char*> (&value), 8);
+ return 9;
+ }
+}
+
+template<class InputIterator>
+inline uint64_t
+readNonNegativeInteger(size_t size, InputIterator &begin, const InputIterator &end)
+{
+ switch (size) {
+ case 1:
+ {
+ if (end - begin < 1)
+ throw Error("Insufficient data during TLV processing");
+
+ uint8_t value = *begin;
+ begin++;
+ return value;
+ }
+ case 2:
+ {
+ if (end - begin < 2)
+ throw Error("Insufficient data during TLV processing");
+
+ uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
+ begin += 2;
+ return be16toh(value);
+ }
+ case 4:
+ {
+ if (end - begin < 4)
+ throw Error("Insufficient data during TLV processing");
+
+ uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
+ begin += 4;
+ return be32toh(value);
+ }
+ case 8:
+ {
+ if (end - begin < 8)
+ throw Error("Insufficient data during TLV processing");
+
+ uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
+ begin += 8;
+ return be64toh(value);
+ }
+ }
+ throw Error("Invalid length for nonNegativeInteger (only 1, 2, 4, and 8 are allowed)");
+}
+
+inline size_t
+sizeOfNonNegativeInteger(uint64_t varNumber)
+{
+ if (varNumber < 253) {
+ return 1;
+ }
+ else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+ return 2;
+ }
+ else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+ return 4;
+ }
+ else {
+ return 8;
+ }
+}
+
+
+inline size_t
+writeNonNegativeInteger(std::ostream &os, uint64_t varNumber)
+{
+ if (varNumber < 253) {
+ os.put(static_cast<uint8_t> (varNumber));
+ return 1;
+ }
+ else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+ uint16_t value = htobe16(static_cast<uint16_t> (varNumber));
+ os.write(reinterpret_cast<const char*> (&value), 2);
+ return 2;
+ }
+ else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+ uint32_t value = htobe32(static_cast<uint32_t> (varNumber));
+ os.write(reinterpret_cast<const char*> (&value), 4);
+ return 4;
+ }
+ else {
+ uint64_t value = htobe64(varNumber);
+ os.write(reinterpret_cast<const char*> (&value), 8);
+ return 8;
+ }
+}
+
+
+} // namespace tlv
+} // namespace ndn
+
+#endif // NDN_TLV_HPP
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
new file mode 100644
index 0000000..c19cee7
--- /dev/null
+++ b/src/encoding/block.cpp
@@ -0,0 +1,210 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include <ndn-cpp/encoding/block.hpp>
+#include <ndn-cpp/encoding/tlv.hpp>
+
+namespace ndn {
+
+Block::Block()
+ : m_type(std::numeric_limits<uint32_t>::max())
+{
+}
+
+Block::Block(const ConstBufferPtr &wire,
+ uint32_t type,
+ const Buffer::const_iterator &begin, Buffer::const_iterator &end,
+ const Buffer::const_iterator &valueBegin, Buffer::const_iterator &valueEnd)
+ : m_buffer(wire)
+ , m_type(type)
+ , m_begin(begin)
+ , m_end(end)
+ , m_size(m_end - m_begin)
+ , m_value_begin(valueBegin)
+ , m_value_end(valueEnd)
+{
+}
+
+Block::Block(const ConstBufferPtr &buffer)
+ : m_buffer(buffer)
+ , m_begin(m_buffer->begin())
+ , m_end(m_buffer->end())
+ , m_size(m_end - m_begin)
+{
+ m_value_begin = m_buffer->begin();
+ m_value_end = m_buffer->end();
+
+ m_type = Tlv::readType(m_value_begin, m_value_end);
+
+ uint64_t length = Tlv::readVarNumber(m_value_begin, m_value_end);
+ if (length != (m_value_end - m_value_begin))
+ {
+ throw Tlv::Error("TLV length doesn't match buffer length");
+ }
+}
+
+Block::Block(const uint8_t *buffer, size_t maxlength)
+{
+ const uint8_t * tmp_begin = buffer;
+ const uint8_t * tmp_end = buffer + maxlength;
+
+ m_type = Tlv::readType(tmp_begin, tmp_end);
+ uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
+
+ if (length > (tmp_end - tmp_begin))
+ {
+ throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
+ }
+
+ m_buffer = ptr_lib::make_shared<Buffer> (buffer, (tmp_begin - buffer) + length);
+
+ m_begin = m_buffer->begin();
+ m_end = m_buffer->end();
+ m_size = m_end - m_begin;
+
+ m_value_begin = m_buffer->begin() + (tmp_begin - buffer);
+ m_value_end = m_buffer->end();
+}
+
+Block::Block(const void *bufferX, size_t maxlength)
+{
+ const uint8_t * buffer = reinterpret_cast<const uint8_t*>(bufferX);
+
+ const uint8_t * tmp_begin = buffer;
+ const uint8_t * tmp_end = buffer + maxlength;
+
+ m_type = Tlv::readType(tmp_begin, tmp_end);
+ uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
+
+ if (length > (tmp_end - tmp_begin))
+ {
+ throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
+ }
+
+ m_buffer = ptr_lib::make_shared<Buffer> (buffer, (tmp_begin - buffer) + length);
+
+ m_begin = m_buffer->begin();
+ m_end = m_buffer->end();
+ m_size = m_end - m_begin;
+
+ m_value_begin = m_buffer->begin() + (tmp_begin - buffer);
+ m_value_end = m_buffer->end();
+}
+
+Block::Block(uint32_t type)
+ : m_type(type)
+{
+}
+
+Block::Block(uint32_t type, const ConstBufferPtr &value)
+ : m_buffer(value)
+ , m_type(type)
+ , m_begin(m_buffer->end())
+ , m_end(m_buffer->end())
+ , m_value_begin(m_buffer->begin())
+ , m_value_end(m_buffer->end())
+{
+ m_size = Tlv::sizeOfVarNumber(m_type) + Tlv::sizeOfVarNumber(value_size()) + value_size();
+}
+
+Block::Block(uint32_t type, const Block &value)
+ : m_buffer(value.m_buffer)
+ , m_type(type)
+ , m_begin(m_buffer->end())
+ , m_end(m_buffer->end())
+ , m_value_begin(m_buffer->begin())
+ , m_value_end(m_buffer->end())
+{
+ m_size = Tlv::sizeOfVarNumber(m_type) + Tlv::sizeOfVarNumber(value_size()) + value_size();
+}
+
+void
+Block::parse()
+{
+ if (!m_subBlocks.empty() || value_size()==0)
+ return;
+
+ Buffer::const_iterator begin = value_begin(),
+ end = value_end();
+
+ while (begin != end)
+ {
+ Buffer::const_iterator element_begin = begin;
+
+ uint32_t type = Tlv::readType(begin, end);
+ uint64_t length = Tlv::readVarNumber(begin, end);
+
+ if (end-begin < length)
+ {
+ m_subBlocks.clear();
+ throw Tlv::Error("TLV length exceeds buffer length");
+ }
+ Buffer::const_iterator element_end = begin + length;
+
+ m_subBlocks.push_back(Block(m_buffer,
+ type,
+ element_begin, element_end,
+ begin, element_end));
+
+ begin = element_end;
+ // don't do recursive parsing, just the top level
+ }
+}
+
+void
+Block::encode()
+{
+ if (hasWire())
+ return;
+
+ OBufferStream os;
+ Tlv::writeVarNumber(os, type());
+
+ if (hasValue())
+ {
+ Tlv::writeVarNumber(os, value_size());
+ os.write(reinterpret_cast<const char*>(value()), value_size());
+ }
+ else
+ {
+ size_t valueSize = 0;
+ for (element_const_iterator i = m_subBlocks.begin(); i != m_subBlocks.end(); ++i) {
+ valueSize += i->size();
+ }
+
+ Tlv::writeVarNumber(os, valueSize);
+
+ for (element_const_iterator i = m_subBlocks.begin(); i != m_subBlocks.end(); ++i) {
+ if (i->hasWire())
+ os.write(reinterpret_cast<const char*>(i->wire()), i->size());
+ else if (i->hasValue()) {
+ Tlv::writeVarNumber(os, i->type());
+ Tlv::writeVarNumber(os, i->value_size());
+ os.write(reinterpret_cast<const char*>(i->value()), i->value_size());
+ }
+ else
+ throw Error("Underlying value buffer is empty");
+ }
+ }
+
+ // now assign correct block
+
+ m_buffer = os.buf();
+ m_begin = m_buffer->begin();
+ m_end = m_buffer->end();
+ m_size = m_end - m_begin;
+
+ m_value_begin = m_buffer->begin();
+ m_value_end = m_buffer->end();
+
+ Tlv::readType(m_value_begin, m_value_end);
+ Tlv::readVarNumber(m_value_begin, m_value_end);
+}
+
+} // namespace ndn