encoding: reorganize Block class code
* Improve Doxygen.
* Consolidate and simplify constructors.
* Reorder class methods in cpp file to match hpp.
* Make operator== and operator!= non-member functions.
* Improve exception messages.
* Update code style.
refs #4171
Change-Id: I4cc0393f43a24a500b0efff90108ad1b42f5fa47
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index 16c05c5..82b1110 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -23,10 +23,9 @@
#include "block.hpp"
#include "block-helpers.hpp"
-
-#include "tlv.hpp"
-#include "encoding-buffer.hpp"
#include "buffer-stream.hpp"
+#include "encoding-buffer.hpp"
+#include "tlv.hpp"
#include <boost/lexical_cast.hpp>
#include <boost/asio/buffer.hpp>
@@ -45,176 +44,122 @@
const size_t MAX_SIZE_OF_BLOCK_FROM_STREAM = MAX_NDN_PACKET_SIZE;
+// ---- constructor, creation, assignment ----
+
Block::Block()
: m_type(std::numeric_limits<uint32_t>::max())
+ , m_size(0)
{
}
Block::Block(const EncodingBuffer& buffer)
- : m_buffer(const_cast<EncodingBuffer&>(buffer).getBuffer())
- , m_begin(buffer.begin())
- , m_end(buffer.end())
- , m_size(m_end - m_begin)
-{
- m_value_begin = m_begin;
- m_value_end = m_end;
-
- m_type = tlv::readType(m_value_begin, m_value_end);
- uint64_t length = tlv::readVarNumber(m_value_begin, m_value_end);
- if (length != static_cast<uint64_t>(m_value_end - m_value_begin))
- {
- BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
- }
-}
-
-Block::Block(const ConstBufferPtr& wire,
- uint32_t type,
- const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
- const Buffer::const_iterator& valueBegin, const 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(const_cast<EncodingBuffer&>(buffer).getBuffer(), buffer.begin(), buffer.end(), true)
{
}
Block::Block(const ConstBufferPtr& buffer)
- : m_buffer(buffer)
- , m_begin(m_buffer->begin())
- , m_end(m_buffer->end())
- , m_size(m_end - m_begin)
+ : Block(buffer, buffer->begin(), buffer->end(), true)
{
- m_value_begin = m_begin;
- m_value_end = m_end;
-
- m_type = tlv::readType(m_value_begin, m_value_end);
-
- uint64_t length = tlv::readVarNumber(m_value_begin, m_value_end);
- if (length != static_cast<uint64_t>(m_value_end - m_value_begin))
- {
- BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
- }
}
-Block::Block(const ConstBufferPtr& buffer,
- const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
- bool verifyLength/* = true*/)
- : m_buffer(buffer)
+Block::Block(ConstBufferPtr buffer, Buffer::const_iterator begin, Buffer::const_iterator end,
+ bool verifyLength)
+ : m_buffer(std::move(buffer))
, m_begin(begin)
, m_end(end)
+ , m_valueBegin(m_begin)
+ , m_valueEnd(m_end)
, m_size(m_end - m_begin)
{
- m_value_begin = m_begin;
- m_value_end = m_end;
+ if (m_buffer->size() == 0) {
+ BOOST_THROW_EXCEPTION(std::invalid_argument("buffer is empty"));
+ }
- m_type = tlv::readType(m_value_begin, m_value_end);
- uint64_t length = tlv::readVarNumber(m_value_begin, m_value_end);
- if (verifyLength) {
- if (length != static_cast<uint64_t>(std::distance(m_value_begin, m_value_end))) {
- BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
- }
+ const uint8_t* bufferBegin = &m_buffer->front();
+ const uint8_t* bufferEnd = bufferBegin + m_buffer->size();
+ if (&*begin < bufferBegin || &*begin > bufferEnd ||
+ &*end < bufferBegin || &*end > bufferEnd) {
+ BOOST_THROW_EXCEPTION(std::invalid_argument("begin/end iterators points out of the buffer"));
+ }
+
+ m_type = tlv::readType(m_valueBegin, m_valueEnd);
+ uint64_t length = tlv::readVarNumber(m_valueBegin, m_valueEnd);
+ // m_valueBegin now points to TLV-VALUE
+
+ if (verifyLength && length != static_cast<uint64_t>(m_valueEnd - m_valueBegin)) {
+ BOOST_THROW_EXCEPTION(Error("TLV-LENGTH doesn't match buffer size"));
}
}
-Block::Block(const Block& block,
- const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
- bool verifyLength/* = true*/)
- : m_buffer(block.m_buffer)
+Block::Block(const Block& block, Buffer::const_iterator begin, Buffer::const_iterator end,
+ bool verifyLength)
+ : Block(block.m_buffer, begin, end, verifyLength)
+{
+}
+
+Block::Block(ConstBufferPtr buffer, uint32_t type,
+ Buffer::const_iterator begin, Buffer::const_iterator end,
+ Buffer::const_iterator valueBegin, Buffer::const_iterator valueEnd)
+ : m_buffer(std::move(buffer))
, m_begin(begin)
, m_end(end)
+ , m_valueBegin(valueBegin)
+ , m_valueEnd(valueEnd)
+ , m_type(type)
, m_size(m_end - m_begin)
{
- if (!(m_buffer->begin() <= begin && begin <= m_buffer->end()) ||
- !(m_buffer->begin() <= end && end <= m_buffer->end())) {
- BOOST_THROW_EXCEPTION(Error("begin/end iterators do not point to the underlying buffer of the block"));
- }
-
- m_value_begin = m_begin;
- m_value_end = m_end;
-
- m_type = tlv::readType(m_value_begin, m_value_end);
- uint64_t length = tlv::readVarNumber(m_value_begin, m_value_end);
- if (verifyLength) {
- if (length != static_cast<uint64_t>(std::distance(m_value_begin, m_value_end))) {
- BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
- }
- }
}
-Block::Block(const uint8_t* buffer, size_t maxlength)
+Block::Block(const uint8_t* buf, size_t bufSize)
{
- const uint8_t* tmp_begin = buffer;
- const uint8_t* tmp_end = buffer + maxlength;
+ const uint8_t* pos = buf;
+ const uint8_t* const end = buf + bufSize;
- m_type = tlv::readType(tmp_begin, tmp_end);
- uint64_t length = tlv::readVarNumber(tmp_begin, tmp_end);
+ m_type = tlv::readType(pos, end);
+ uint64_t length = tlv::readVarNumber(pos, end);
+ // pos now points to TLV-VALUE
- if (length > static_cast<uint64_t>(tmp_end - tmp_begin))
- {
- BOOST_THROW_EXCEPTION(tlv::Error("Not enough data in the buffer to fully parse TLV"));
- }
+ if (length > static_cast<uint64_t>(end - pos)) {
+ BOOST_THROW_EXCEPTION(tlv::Error("Not enough data in the buffer to fully parse TLV"));
+ }
+ size_t typeLengthSize = pos - buf;
+ m_size = typeLengthSize + length;
- m_buffer = make_shared<Buffer>(buffer, (tmp_begin - buffer) + length);
-
+ m_buffer = make_shared<Buffer>(buf, m_size);
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();
+ m_end = m_valueEnd = m_buffer->end();
+ m_valueBegin = m_begin + typeLengthSize;
}
-Block::Block(const void* bufferX, size_t maxlength)
+Block::Block(const void* buf, size_t bufSize)
+ : Block(reinterpret_cast<const uint8_t*>(buf), bufSize)
{
- 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 > static_cast<uint64_t>(tmp_end - tmp_begin))
- {
- BOOST_THROW_EXCEPTION(tlv::Error("Not enough data in the buffer to fully parse TLV"));
- }
-
- m_buffer = 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)
+ , m_size(tlv::sizeOfVarNumber(m_type) + tlv::sizeOfVarNumber(0))
{
}
-Block::Block(uint32_t type, const ConstBufferPtr& value)
- : m_buffer(value)
- , m_type(type)
+Block::Block(uint32_t type, ConstBufferPtr value)
+ : m_buffer(std::move(value))
, m_begin(m_buffer->end())
, m_end(m_buffer->end())
- , m_value_begin(m_buffer->begin())
- , m_value_end(m_buffer->end())
+ , m_valueBegin(m_buffer->begin())
+ , m_valueEnd(m_buffer->end())
+ , m_type(type)
{
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(value.begin())
- , m_value_end(value.end())
+ , m_valueBegin(value.begin())
+ , m_valueEnd(value.end())
+ , m_type(type)
{
m_size = tlv::sizeOfVarNumber(m_type) + tlv::sizeOfVarNumber(value_size()) + value_size();
}
@@ -229,11 +174,13 @@
uint64_t length = tlv::readVarNumber(begin, end);
if (length == 0) {
- return makeEmptyBlock(type);
+ // XXX An extra octet is incorrectly consumed from istream (#4180)
+ return Block(type);
}
- if (length > MAX_SIZE_OF_BLOCK_FROM_STREAM)
- BOOST_THROW_EXCEPTION(tlv::Error("Length of block from stream is too large"));
+ if (length > MAX_SIZE_OF_BLOCK_FROM_STREAM) {
+ BOOST_THROW_EXCEPTION(tlv::Error("TLV-LENGTH from stream exceeds limit"));
+ }
// We may still have some problem here, if some exception happens,
// we may completely lose all the bytes extracted from the stream.
@@ -252,225 +199,78 @@
std::tuple<bool, Block>
Block::fromBuffer(ConstBufferPtr buffer, size_t offset)
{
- Buffer::const_iterator tempBegin = buffer->begin() + offset;
+ const Buffer::const_iterator begin = buffer->begin() + offset;
+ Buffer::const_iterator pos = begin;
- uint32_t type;
- bool isOk = tlv::readType(tempBegin, buffer->end(), type);
- if (!isOk)
+ uint32_t type = 0;
+ bool isOk = tlv::readType(pos, buffer->end(), type);
+ if (!isOk) {
return std::make_tuple(false, Block());
-
- uint64_t length;
- isOk = tlv::readVarNumber(tempBegin, buffer->end(), length);
- if (!isOk)
+ }
+ uint64_t length = 0;
+ isOk = tlv::readVarNumber(pos, buffer->end(), length);
+ if (!isOk) {
return std::make_tuple(false, Block());
+ }
+ // pos now points to TLV-VALUE
- if (length > static_cast<uint64_t>(buffer->end() - tempBegin))
+ if (length > static_cast<uint64_t>(buffer->end() - pos)) {
return std::make_tuple(false, Block());
+ }
- return std::make_tuple(true, Block(buffer, type,
- buffer->begin() + offset, tempBegin + length,
- tempBegin, tempBegin + length));
+ return std::make_tuple(true, Block(buffer, type, begin, pos + length, pos, pos + length));
}
std::tuple<bool, Block>
-Block::fromBuffer(const uint8_t* buffer, size_t maxSize)
+Block::fromBuffer(const uint8_t* buf, size_t bufSize)
{
- const uint8_t* tempBegin = buffer;
- const uint8_t* tempEnd = buffer + maxSize;
+ const uint8_t* pos = buf;
+ const uint8_t* const end = buf + bufSize;
uint32_t type = 0;
- bool isOk = tlv::readType(tempBegin, tempEnd, type);
- if (!isOk)
+ bool isOk = tlv::readType(pos, end, type);
+ if (!isOk) {
return std::make_tuple(false, Block());
-
- uint64_t length;
- isOk = tlv::readVarNumber(tempBegin, tempEnd, length);
- if (!isOk)
+ }
+ uint64_t length = 0;
+ isOk = tlv::readVarNumber(pos, end, length);
+ if (!isOk) {
return std::make_tuple(false, Block());
+ }
+ // pos now points to TLV-VALUE
- if (length > static_cast<uint64_t>(tempEnd - tempBegin))
+ if (length > static_cast<uint64_t>(end - pos)) {
return std::make_tuple(false, Block());
+ }
- BufferPtr sharedBuffer = make_shared<Buffer>(buffer, tempBegin + length);
- return std::make_tuple(true,
- Block(sharedBuffer, type,
- sharedBuffer->begin(), sharedBuffer->end(),
- sharedBuffer->begin() + (tempBegin - buffer), sharedBuffer->end()));
+ size_t typeLengthSize = pos - buf;
+ auto b = make_shared<Buffer>(buf, pos + length);
+ return std::make_tuple(true, Block(b, type, b->begin(), b->end(),
+ b->begin() + typeLengthSize, b->end()));
+}
+
+// ---- wire format ----
+
+bool
+Block::hasWire() const
+{
+ return m_buffer != nullptr && m_begin != m_end;
}
void
Block::reset()
{
- m_buffer.reset(); // reset of the shared_ptr
- m_subBlocks.clear(); // remove all parsed subelements
+ this->resetWire();
m_type = std::numeric_limits<uint32_t>::max();
- m_begin = m_end = m_value_begin = m_value_end = Buffer::const_iterator();
+ m_elements.clear();
}
void
Block::resetWire()
{
- m_buffer.reset(); // reset of the shared_ptr
- // keep subblocks
-
- // keep type
- m_begin = m_end = m_value_begin = m_value_end = Buffer::const_iterator();
-}
-
-void
-Block::parse() const
-{
- if (!m_subBlocks.empty() || value_size() == 0)
- return;
-
- Buffer::const_iterator begin = value_begin();
- Buffer::const_iterator 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 (length > static_cast<uint64_t>(end - begin))
- {
- m_subBlocks.clear();
- BOOST_THROW_EXCEPTION(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 if (m_subBlocks.size() == 0)
- {
- tlv::writeVarNumber(os, 0);
- }
- 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
- BOOST_THROW_EXCEPTION(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);
-}
-
-const Block&
-Block::get(uint32_t type) const
-{
- element_const_iterator it = this->find(type);
- if (it != m_subBlocks.end())
- return *it;
-
- BOOST_THROW_EXCEPTION(Error("(Block::get) Requested a non-existed type [" +
- boost::lexical_cast<std::string>(type) + "] from Block"));
-}
-
-Block::element_const_iterator
-Block::find(uint32_t type) const
-{
- return std::find_if(m_subBlocks.begin(), m_subBlocks.end(),
- [type] (const Block& subBlock) { return subBlock.type() == type; });
-}
-
-void
-Block::remove(uint32_t type)
-{
- resetWire();
-
- auto it = std::remove_if(m_subBlocks.begin(), m_subBlocks.end(),
- [type] (const Block& subBlock) { return subBlock.type() == type; });
- m_subBlocks.resize(it - m_subBlocks.begin());
-}
-
-Block
-Block::blockFromValue() const
-{
- if (value_size() == 0)
- BOOST_THROW_EXCEPTION(Error("Underlying value buffer is empty"));
-
- Buffer::const_iterator begin = value_begin(),
- end = value_end();
-
- Buffer::const_iterator element_begin = begin;
-
- uint32_t type = tlv::readType(begin, end);
- uint64_t length = tlv::readVarNumber(begin, end);
-
- if (length != static_cast<uint64_t>(end - begin))
- BOOST_THROW_EXCEPTION(tlv::Error("TLV length mismatches buffer length"));
-
- return Block(m_buffer,
- type,
- element_begin, end,
- begin, end);
-}
-
-Block::operator boost::asio::const_buffer() const
-{
- return boost::asio::const_buffer(wire(), size());
-}
-
-bool
-Block::empty() const
-{
- return m_type == std::numeric_limits<uint32_t>::max();
-}
-
-bool
-Block::hasWire() const
-{
- return m_buffer && (m_begin != m_end);
+ m_buffer.reset(); // discard underlying buffer by resetting shared_ptr
+ m_begin = m_end = m_valueBegin = m_valueEnd = Buffer::const_iterator();
}
Buffer::const_iterator
@@ -495,7 +295,7 @@
Block::wire() const
{
if (!hasWire())
- BOOST_THROW_EXCEPTION(Error("(Block::wire) Underlying wire buffer is empty"));
+ BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
return &*m_begin;
}
@@ -503,35 +303,144 @@
size_t
Block::size() const
{
- if (hasWire() || hasValue()) {
- return m_size;
- }
- else
+ if (empty()) {
BOOST_THROW_EXCEPTION(Error("Block size cannot be determined (undefined block size)"));
+ }
+
+ return m_size;
}
-bool
-Block::hasValue() const
-{
- return static_cast<bool>(m_buffer);
-}
+// ---- value ----
const uint8_t*
Block::value() const
{
- if (!hasValue())
- return 0;
-
- return &*m_value_begin;
+ return hasValue() ? &*m_valueBegin : nullptr;
}
size_t
Block::value_size() const
{
- if (!hasValue())
- return 0;
+ return hasValue() ? m_valueEnd - m_valueBegin : 0;
+}
- return m_value_end - m_value_begin;
+Block
+Block::blockFromValue() const
+{
+ if (!hasValue())
+ BOOST_THROW_EXCEPTION(Error("Block has no TLV-VALUE"));
+
+ return Block(*this, m_valueBegin, m_valueEnd, true);
+}
+
+// ---- sub elements ----
+
+void
+Block::parse() const
+{
+ if (!m_elements.empty() || value_size() == 0)
+ return;
+
+ Buffer::const_iterator begin = value_begin();
+ Buffer::const_iterator end = value_end();
+
+ while (begin != end) {
+ Buffer::const_iterator pos = begin;
+
+ uint32_t type = tlv::readType(pos, end);
+ uint64_t length = tlv::readVarNumber(pos, end);
+ if (length > static_cast<uint64_t>(end - pos)) {
+ m_elements.clear();
+ BOOST_THROW_EXCEPTION(Error("TLV-LENGTH of sub-element of type " + to_string(type) +
+ " exceeds TLV-VALUE boundary of parent block"));
+ }
+ // pos now points to TLV-VALUE of sub element
+
+ Buffer::const_iterator subEnd = pos + length;
+ m_elements.emplace_back(m_buffer, type, begin, subEnd, pos, subEnd);
+
+ begin = subEnd;
+ }
+}
+
+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 if (m_elements.size() == 0) {
+ tlv::writeVarNumber(os, 0);
+ }
+ else {
+ size_t valueSize = 0;
+ for (element_const_iterator i = m_elements.begin(); i != m_elements.end(); ++i) {
+ valueSize += i->size();
+ }
+
+ tlv::writeVarNumber(os, valueSize);
+
+ for (element_const_iterator i = m_elements.begin(); i != m_elements.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
+ BOOST_THROW_EXCEPTION(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_valueBegin = m_buffer->begin();
+ m_valueEnd = m_buffer->end();
+
+ tlv::readType(m_valueBegin, m_valueEnd);
+ tlv::readVarNumber(m_valueBegin, m_valueEnd);
+}
+
+const Block&
+Block::get(uint32_t type) const
+{
+ auto it = this->find(type);
+ if (it != m_elements.end()) {
+ return *it;
+ }
+
+ BOOST_THROW_EXCEPTION(Error("No sub-element of type " + to_string(type) +
+ " is found in block of type " + to_string(m_type)));
+}
+
+Block::element_const_iterator
+Block::find(uint32_t type) const
+{
+ return std::find_if(m_elements.begin(), m_elements.end(),
+ [type] (const Block& subBlock) { return subBlock.type() == type; });
+}
+
+void
+Block::remove(uint32_t type)
+{
+ resetWire();
+
+ auto it = std::remove_if(m_elements.begin(), m_elements.end(),
+ [type] (const Block& subBlock) { return subBlock.type() == type; });
+ m_elements.resize(it - m_elements.begin());
}
Block::element_iterator
@@ -540,11 +449,11 @@
resetWire();
#ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
- return m_subBlocks.erase(position);
+ return m_elements.erase(position);
#else
- element_iterator it = m_subBlocks.begin();
- std::advance(it, std::distance(m_subBlocks.cbegin(), position));
- return m_subBlocks.erase(it);
+ element_iterator it = m_elements.begin();
+ std::advance(it, std::distance(m_elements.cbegin(), position));
+ return m_elements.erase(it);
#endif
}
@@ -554,13 +463,13 @@
resetWire();
#ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
- return m_subBlocks.erase(first, last);
+ return m_elements.erase(first, last);
#else
- element_iterator itStart = m_subBlocks.begin();
- element_iterator itEnd = m_subBlocks.begin();
- std::advance(itStart, std::distance(m_subBlocks.cbegin(), first));
- std::advance(itEnd, std::distance(m_subBlocks.cbegin(), last));
- return m_subBlocks.erase(itStart, itEnd);
+ element_iterator itStart = m_elements.begin();
+ element_iterator itEnd = m_elements.begin();
+ std::advance(itStart, std::distance(m_elements.cbegin(), first));
+ std::advance(itEnd, std::distance(m_elements.cbegin(), last));
+ return m_elements.erase(itStart, itEnd);
#endif
}
@@ -568,7 +477,7 @@
Block::push_back(const Block& element)
{
resetWire();
- m_subBlocks.push_back(element);
+ m_elements.push_back(element);
}
Block::element_iterator
@@ -577,43 +486,27 @@
resetWire();
#ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
- return m_subBlocks.insert(pos, element);
+ return m_elements.insert(pos, element);
#else
- element_iterator it = m_subBlocks.begin();
- std::advance(it, std::distance(m_subBlocks.cbegin(), pos));
- return m_subBlocks.insert(it, element);
+ element_iterator it = m_elements.begin();
+ std::advance(it, std::distance(m_elements.cbegin(), pos));
+ return m_elements.insert(it, element);
#endif
}
-Block::element_const_iterator
-Block::elements_begin() const
-{
- return m_subBlocks.begin();
-}
+// ---- misc ----
-Block::element_const_iterator
-Block::elements_end() const
+Block::operator boost::asio::const_buffer() const
{
- return m_subBlocks.end();
-}
-
-size_t
-Block::elements_size() const
-{
- return m_subBlocks.size();
+ return boost::asio::const_buffer(wire(), size());
}
bool
-Block::operator!=(const Block& other) const
+operator==(const Block& lhs, const Block& rhs)
{
- return !this->operator==(other);
-}
-
-bool
-Block::operator==(const Block& other) const
-{
- return this->size() == other.size() &&
- std::equal(this->begin(), this->end(), other.begin());
+ return lhs.type() == rhs.type() &&
+ lhs.value_size() == rhs.value_size() &&
+ ::memcmp(lhs.value(), rhs.value(), lhs.value_size()) == 0;
}
} // namespace ndn
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index 7ade6e3..a5f1e1a 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -43,9 +43,9 @@
class Block
{
public:
- typedef std::vector<Block> element_container;
- typedef element_container::iterator element_iterator;
- typedef element_container::const_iterator element_const_iterator;
+ using element_container = std::vector<Block>;
+ using element_iterator = element_container::iterator;
+ using element_const_iterator = element_container::const_iterator;
class Error : public tlv::Error
{
@@ -62,286 +62,366 @@
*/
Block();
- /** @brief Create a Block based on EncodingBuffer object
+ /** @brief Parse Block from an EncodingBuffer
+ * @param buffer an EncodingBuffer containing one TLV element
+ * @throw tlv::Error Type-Length parsing fails, or TLV-LENGTH does not match size of TLV-VALUE
*/
explicit
Block(const EncodingBuffer& buffer);
- /** @brief Create a Block from the raw buffer with Type-Length parsing
+ /** @brief Parse Block from a wire Buffer
+ * @param buffer a Buffer containing one TLV element
+ * @note This constructor takes shared ownership of @p buffer.
+ * @throw tlv::Error Type-Length parsing fails, or TLV-LENGTH does not match size of TLV-VALUE
*/
explicit
Block(const ConstBufferPtr& buffer);
- /** @brief Create a Block from a buffer, directly specifying boundaries
- * of the block within the buffer
- *
- * This overload will automatically detect type and position of the value within the block
+ /** @brief Parse Block within boundaries of a wire Buffer
+ * @param buffer a Buffer containing an TLV element at [@p begin,@p end)
+ * @param begin begin position of the TLV element within @p buffer
+ * @param end end position of the TLV element within @p buffer
+ * @param verifyLength if true, check TLV-LENGTH equals size of TLV-VALUE
+ * @throw std::invalid_argument @p buffer is empty, or [@p begin,@p end) range are not within @p buffer
+ * @throw tlv::Error Type-Length parsing fails, or TLV-LENGTH does not match size of TLV-VALUE
+ * @note This overload automatically detects TLV-TYPE and position of TLV-VALUE.
*/
- Block(const ConstBufferPtr& buffer,
- const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
+ Block(ConstBufferPtr buffer, Buffer::const_iterator begin, Buffer::const_iterator end,
bool verifyLength = true);
- /** @brief Create a Block from existing block (reusing the underlying buffer), directly
- * specifying boundaries of the block within the buffer
- *
- * This overload will automatically detect type and position of the value within the block
+ /** @brief Parse Block within boundaries of an existing Block, reusing underlying wire Buffer
+ * @param block a Block whose buffer contains an TLV element at [@p begin,@p end)
+ * @param begin begin position of the TLV element within @p block
+ * @param end end position of the TLV element within @p block
+ * @param verifyLength if true, check TLV-LENGTH equals size of TLV-VALUE
+ * @throw std::invalid_argument [@p begin,@p end) range are not within @p block
+ * @throw tlv::Error Type-Length parsing fails, or TLV-LENGTH does not match size of TLV-VALUE
*/
- Block(const Block& block,
- const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
+ Block(const Block& block, Buffer::const_iterator begin, Buffer::const_iterator end,
bool verifyLength = true);
- /** @brief Create a Block from the raw buffer with Type-Length parsing
+ /** @brief Create a Block from a wire Buffer without parsing
+ * @param buffer a Buffer containing an TLV element at [@p begin,@p end)
+ * @param type TLV-TYPE
+ * @param begin begin position of the TLV element within @p buffer
+ * @param end end position of the TLV element within @p buffer
+ * @param valueBegin begin position of TLV-VALUE within @p buffer
+ * @param valueEnd end position of TLV-VALUE within @p buffer
*/
- Block(const uint8_t* buffer, size_t maxlength);
+ Block(ConstBufferPtr buffer, uint32_t type,
+ Buffer::const_iterator begin, Buffer::const_iterator end,
+ Buffer::const_iterator valueBegin, Buffer::const_iterator valueEnd);
- /** @brief Create a Block from the raw buffer with Type-Length parsing
+ /** @brief Parse Block from a raw buffer
+ * @param buf pointer to the first octet of an TLV element
+ * @param bufSize size of the raw buffer; may be more than size of the TLV element
+ * @throw tlv::Error Type-Length parsing fails, or size of TLV-VALUE exceeds @p bufSize
+ * @note This overload copies the TLV element into an internal wire buffer.
*/
- Block(const void* buffer, size_t maxlength);
+ Block(const uint8_t* buf, size_t bufSize);
- /** @brief Create a Block from the wire buffer (no parsing)
- *
- * This overload does not do any parsing
+ /** @brief Parse Block from a raw buffer
+ * @deprecated use Block(const uint8_t*, size_t)
*/
- Block(const ConstBufferPtr& wire,
- uint32_t type,
- const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
- const Buffer::const_iterator& valueBegin, const Buffer::const_iterator& valueEnd);
+ Block(const void* buf, size_t bufSize);
- /** @brief Create Block of a specific type with empty wire buffer
+ /** @brief Create an empty Block with specified TLV-TYPE
+ * @param type TLV-TYPE
*/
explicit
Block(uint32_t type);
- /** @brief Create a Block of a specific type with the specified value
- *
- * The underlying buffer holds only value Additional operations are needed
- * to construct wire encoding, one need to prepend the wire buffer with type
- * and value-length VAR-NUMBERs
+ /** @brief Create a Block with specified TLV-TYPE and TLV-VALUE
+ * @param type TLV-TYPE
+ * @param value a Buffer containing the TLV-VALUE
*/
- Block(uint32_t type, const ConstBufferPtr& value);
+ Block(uint32_t type, ConstBufferPtr value);
- /** @brief Create a nested Block of a specific type with the specified value
- *
- * The underlying buffer holds only value. Additional operations are needed
- * to construct wire encoding, one need to prepend the wire buffer with type
- * and value-length VAR-NUMBERs
+ /** @brief Create a Block with specified TLV-TYPE and TLV-VALUE
+ * @param type TLV-TYPE
+ * @param value a Block to be nested as TLV-VALUE
*/
Block(uint32_t type, const Block& value);
- /** @brief Create a Block from an input stream
+ /** @brief Parse Block from an input stream
*/
static Block
fromStream(std::istream& is);
- /** @brief Try to construct block from Buffer
- * @param buffer the buffer to construct block from
- * @note buffer is passed by value because the constructed block
- * takes shared ownership of the buffer
- * @param offset offset from beginning of \p buffer to construct Block from
- *
- * This method does not throw upon decoding error.
- * This method does not copy the bytes.
- *
- * @return true and the Block, if Block is successfully created; otherwise false
+ /** @brief Try to parse Block from a wire buffer
+ * @param buffer a Buffer containing an TLV element at offset @p offset
+ * @param offset begin position of the TLV element within @p buffer
+ * @note This function does not throw exceptions upon decoding failure.
+ * @return true and the Block if parsing succeeds; otherwise false
*/
static std::tuple<bool, Block>
fromBuffer(ConstBufferPtr buffer, size_t offset);
- /** @brief Try to construct block from raw buffer
- * @param buffer the raw buffer to copy bytes from
- * @param maxSize the maximum size of constructed block;
- * @p buffer must have a size of at least @p maxSize
- *
- * This method does not throw upon decoding error.
- * This method copies the bytes into a new Buffer.
- *
- * @return true and the Block, if Block is successfully created; otherwise false
+ /** @brief Try to parse Block from a raw buffer
+ * @param buf pointer to the first octet of an TLV element
+ * @param bufSize size of the raw buffer; may be more than size of the TLV element
+ * @note This function does not throw exceptions upon decoding failure.
+ * @note This overload copies the TLV element into an internal wire buffer.
+ * @return true and the Block if parsing succeeds; otherwise false
*/
static std::tuple<bool, Block>
- fromBuffer(const uint8_t* buffer, size_t maxSize);
+ fromBuffer(const uint8_t* buf, size_t bufSize);
public: // wire format
/** @brief Check if the Block is empty
+ *
+ * A Block is "empty" only if it is default-constructed. A Block with zero-length TLV-VALUE is
+ * not considered empty.
*/
bool
- empty() const;
+ empty() const
+ {
+ return m_type == std::numeric_limits<uint32_t>::max();
+ }
/** @brief Check if the Block has fully encoded wire
+ *
+ * A Block has fully encoded wire if the underlying buffer exists and contains full
+ * Type-Length-Value instead of just TLV-VALUE field.
*/
bool
hasWire() const;
/** @brief Reset wire buffer of the element
+ * @post empty() == true
*/
void
reset();
- /** @brief Reset wire buffer but keep sub elements (if any)
+ /** @brief Reset wire buffer but keep TLV-TYPE and sub elements (if any)
+ * @post hasWire() == false
+ * @post hasValue() == false
*/
void
resetWire();
+ /** @brief Get begin iterator of encoded wire
+ * @pre hasWire() == true
+ */
Buffer::const_iterator
begin() const;
+ /** @brief Get end iterator of encoded wire
+ * @pre hasWire() == true
+ */
Buffer::const_iterator
end() const;
+ /** @brief Get pointer to encoded wire
+ * @pre hasWire() == true
+ */
const uint8_t*
wire() const;
+ /** @brief Get size of encoded wire, including Type-Length-Value
+ * @pre empty() == false
+ */
size_t
size() const;
-public: // type and value
- uint32_t
- type() const;
+ /** @brief Get underlying buffer
+ */
+ shared_ptr<const Buffer>
+ getBuffer() const
+ {
+ return m_buffer;
+ }
- /** @brief Check if the Block has value block (no type and length are encoded)
+public: // type and value
+ /** @brief Get TLV-TYPE
+ */
+ uint32_t
+ type() const
+ {
+ return m_type;
+ }
+
+ /** @brief Get begin iterator of TLV-VALUE
+ *
+ * This property reflects whether the underlying buffer contains TLV-VALUE. If this is false,
+ * TLV-VALUE has zero-length. If this is true, TLV-VALUE may be zero-length.
*/
bool
- hasValue() const;
+ hasValue() const
+ {
+ return m_buffer != nullptr;
+ }
+ /** @brief Get begin iterator of TLV-VALUE
+ * @pre hasValue() == true
+ */
Buffer::const_iterator
- value_begin() const;
+ value_begin() const
+ {
+ return m_valueBegin;
+ }
+ /** @brief Get end iterator of TLV-VALUE
+ * @pre hasValue() == true
+ */
Buffer::const_iterator
- value_end() const;
+ value_end() const
+ {
+ return m_valueEnd;
+ }
+ /** @brief Get pointer to TLV-VALUE
+ */
const uint8_t*
value() const;
+ /** @brief Get size of TLV-VALUE aka TLV-LENGTH
+ */
size_t
value_size() const;
-public: // sub elements
- /** @brief Parse wire buffer into subblocks
- *
- * This method is not really const, but it does not modify any data. It simply
- * parses contents of the buffer into subblocks
- */
- void
- parse() const;
-
- /** @brief Encode subblocks into wire buffer
- */
- void
- encode();
-
- /** @brief Get the first subelement of the requested type
- */
- const Block&
- get(uint32_t type) const;
-
- element_const_iterator
- find(uint32_t type) const;
-
- /**
- * @brief remove all subelements of \p type
- * @param type TLV-TYPE of subelements to remove
- * @pre parse() has been invoked
- */
- void
- remove(uint32_t type);
-
- element_iterator
- erase(element_const_iterator position);
-
- element_iterator
- erase(element_const_iterator first, element_const_iterator last);
-
- void
- push_back(const Block& element);
-
- /**
- * @brief insert Insert a new element in a specific position
- * @param pos Position to insert the new element
- * @param element Element to be inserted
- * @return An iterator that points to the first of the newly inserted elements.
- */
- element_iterator
- insert(element_const_iterator pos, const Block& element);
-
- /** @brief Get all subelements
- */
- const element_container&
- elements() const;
-
- element_const_iterator
- elements_begin() const;
-
- element_const_iterator
- elements_end() const;
-
- size_t
- elements_size() const;
-
Block
blockFromValue() const;
- /**
- * @brief Get underlying buffer
+public: // sub elements
+ /** @brief Parse TLV-VALUE into sub elements
+ * @post elements() reflects sub elements found in TLV-VALUE
+ * @throw tlv::Error TLV-VALUE is not a sequence of TLV elements
+ * @note This method does not perform recursive parsing.
+ * @note This method has no effect if elements() is already populated.
+ * @note This method is not really const, but it does not modify any data.
*/
- shared_ptr<const Buffer>
- getBuffer() const;
+ void
+ parse() const;
-public: // EqualityComparable concept
- bool
- operator==(const Block& other) const;
+ /** @brief Encode sub elements into TLV-VALUE
+ * @post TLV-VALUE contains sub elements from elements()
+ */
+ void
+ encode();
- bool
- operator!=(const Block& other) const;
+ /** @brief Get the first sub element of specified TLV-TYPE
+ * @pre parse() has been executed
+ * @throw Error sub element of @p type does not exist
+ */
+ const Block&
+ get(uint32_t type) const;
-public: // ConvertibleToConstBuffer
+ /** @brief Find the first sub element of specified TLV-TYPE
+ * @pre parse() has been executed
+ * @return iterator in elements() to the found sub element, otherwise elements_end()
+ */
+ element_const_iterator
+ find(uint32_t type) const;
+
+ /** @brief Remove all sub elements of specified TLV-TYPE
+ * @pre parse() has been executed
+ * @post find(type) == elements_end()
+ */
+ void
+ remove(uint32_t type);
+
+ /** @brief Erase a sub element
+ */
+ element_iterator
+ erase(element_const_iterator position);
+
+ /** @brief Erase a range of sub elements
+ */
+ element_iterator
+ erase(element_const_iterator first, element_const_iterator last);
+
+ /** @brief Append a sub element
+ */
+ void
+ push_back(const Block& element);
+
+ /** @brief Insert a sub element
+ * @param pos position of new sub element
+ * @param element new sub element
+ * @return iterator in elements() to the new sub element
+ */
+ element_iterator
+ insert(element_const_iterator pos, const Block& element);
+
+ /** @brief Get container of sub elements
+ * @pre parse() has been executed
+ */
+ const element_container&
+ elements() const
+ {
+ return m_elements;
+ }
+
+ /** @brief Equivalent to elements().begin()
+ */
+ element_const_iterator
+ elements_begin() const
+ {
+ return m_elements.begin();
+ }
+
+ /** @brief Equivalent to elements().end()
+ */
+ element_const_iterator
+ elements_end() const
+ {
+ return m_elements.end();
+ }
+
+ /** @brief Equivalent to elements().size()
+ */
+ size_t
+ elements_size() const
+ {
+ return m_elements.size();
+ }
+
+public: // misc
+ /** @brief Implicit conversion to const_buffer
+ */
operator boost::asio::const_buffer() const;
protected:
+ /** @brief underlying buffer storing TLV-VALUE and possibly TLV-TYPE and TLV-LENGTH fields
+ *
+ * If m_buffer is nullptr, this is an empty Block with TLV-TYPE given in m_type.
+ * Otherwise,
+ * - [m_valueBegin, m_valueEnd) point to TLV-VALUE within m_buffer.
+ * - If m_begin != m_end, [m_begin,m_end) point to Type-Length-Value of this Block within m_buffer.
+ * Otherwise, m_buffer does not contain TLV-TYPE and TLV-LENGTH fields.
+ */
shared_ptr<const Buffer> m_buffer;
+ Buffer::const_iterator m_begin; ///< @sa m_buffer
+ Buffer::const_iterator m_end; ///< @sa m_buffer
- uint32_t m_type;
+ Buffer::const_iterator m_valueBegin; ///< @sa m_buffer
+ Buffer::const_iterator m_valueEnd; ///< @sa m_buffer
- Buffer::const_iterator m_begin;
- Buffer::const_iterator m_end;
+ uint32_t m_type; ///< TLV-TYPE
+
+ /** @brief total size including Type-Length-Value
+ *
+ * This field is valid only if empty() is false.
+ */
uint32_t m_size;
- Buffer::const_iterator m_value_begin;
- Buffer::const_iterator m_value_end;
-
- mutable element_container m_subBlocks;
+ /** @brief sub elements
+ *
+ * This field is valid only if parse() has been executed.
+ */
+ mutable element_container m_elements;
};
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
+/** @brief Compare whether two Blocks have same TLV-TYPE, TLV-LENGTH, and TLV-VALUE
+ */
+bool
+operator==(const Block& lhs, const Block& rhs);
-inline shared_ptr<const Buffer>
-Block::getBuffer() const
+inline bool
+operator!=(const Block& lhs, const Block& rhs)
{
- return m_buffer;
-}
-
-inline uint32_t
-Block::type() const
-{
- return m_type;
-}
-
-inline Buffer::const_iterator
-Block::value_begin() const
-{
- return m_value_begin;
-}
-
-inline Buffer::const_iterator
-Block::value_end() const
-{
- return m_value_end;
-}
-
-inline const Block::element_container&
-Block::elements() const
-{
- return m_subBlocks;
+ return !(lhs == rhs);
}
} // namespace ndn
diff --git a/tests/unit-tests/encoding/block.t.cpp b/tests/unit-tests/encoding/block.t.cpp
index 599f37c..02b1677 100644
--- a/tests/unit-tests/encoding/block.t.cpp
+++ b/tests/unit-tests/encoding/block.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -187,9 +187,9 @@
BOOST_CHECK(derivedBlock == Block(buffer + 2, 3));
Buffer otherBuffer(buffer, sizeof(buffer));
- BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), block.end()), Block::Error);
- BOOST_CHECK_THROW(Block(block, block.begin(), otherBuffer.end()), Block::Error);
- BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), otherBuffer.end()), Block::Error);
+ BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), block.end()), std::invalid_argument);
+ BOOST_CHECK_THROW(Block(block, block.begin(), otherBuffer.end()), std::invalid_argument);
+ BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), otherBuffer.end()), std::invalid_argument);
}
BOOST_AUTO_TEST_CASE(FromBlockCopyOnWriteModifyOriginal)
diff --git a/tests/unit-tests/interest.t.cpp b/tests/unit-tests/interest.t.cpp
index d0345a4..a4c78c0 100644
--- a/tests/unit-tests/interest.t.cpp
+++ b/tests/unit-tests/interest.t.cpp
@@ -200,6 +200,7 @@
Data data("ndn:/A/D");
SignatureSha256WithRsa signature(KeyLocator("ndn:/B"));
+ signature.setValue(encoding::makeEmptyBlock(tlv::SignatureValue));
data.setSignature(signature);
data.wireEncode();
BOOST_CHECK_EQUAL(interest.matchesData(data), true);
@@ -224,6 +225,7 @@
Data data3 = data;
SignatureSha256WithRsa signature3(KeyLocator("ndn:/G")); // violates PublisherPublicKeyLocator
+ signature3.setValue(encoding::makeEmptyBlock(tlv::SignatureValue));
data3.setSignature(signature3);
data3.wireEncode();
BOOST_CHECK_EQUAL(interest.matchesData(data3), false);
@@ -234,6 +236,7 @@
Data data4 = data;
DigestSha256 signature4; // violates PublisherPublicKeyLocator
+ signature4.setValue(encoding::makeEmptyBlock(tlv::SignatureValue));
data4.setSignature(signature4);
data4.wireEncode();
BOOST_CHECK_EQUAL(interest.matchesData(data4), false);
@@ -260,7 +263,7 @@
data7.setName("ndn:/A/B");
data7.wireEncode();
- Interest interest7("/A/B/sha256digest=D548DECEFC4B880720DC9257A8D815E9DF4465E63742EE55C29133055DAA67C2");
+ Interest interest7("/A/B/sha256digest=54008e240a7eea2714a161dfddf0dd6ced223b3856e9da96792151e180f3b128");
BOOST_CHECK_EQUAL(interest7.matchesData(data7), true);
Interest interest7b("/A/B/sha256digest=0000000000000000000000000000000000000000000000000000000000000000");