block.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2018 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  *
21  * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
22  */
23 
24 #include "block.hpp"
25 #include "buffer-stream.hpp"
26 #include "encoding-buffer.hpp"
27 #include "tlv.hpp"
28 #include "../util/string-helper.hpp"
29 
30 #include <boost/asio/buffer.hpp>
31 #include <boost/range/adaptor/reversed.hpp>
32 #include <cstring>
33 
34 namespace ndn {
35 
36 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Block>));
37 #if NDN_CXX_HAVE_IS_NOTHROW_MOVE_CONSTRUCTIBLE
38 static_assert(std::is_nothrow_move_constructible<Block>::value,
39  "Block must be MoveConstructible with noexcept");
40 #endif // NDN_CXX_HAVE_IS_NOTHROW_MOVE_CONSTRUCTIBLE
41 
42 #if NDN_CXX_HAVE_IS_NOTHROW_MOVE_ASSIGNABLE
43 static_assert(std::is_nothrow_move_assignable<Block>::value,
44  "Block must be MoveAssignable with noexcept");
45 #endif // NDN_CXX_HAVE_IS_NOTHROW_MOVE_ASSIGNABLE
46 
48 
49 // ---- constructor, creation, assignment ----
50 
52  : m_type(std::numeric_limits<uint32_t>::max())
53  , m_size(0)
54 {
55 }
56 
58  : Block(const_cast<EncodingBuffer&>(buffer).getBuffer(), buffer.begin(), buffer.end(), true)
59 {
60 }
61 
63  : Block(buffer, buffer->begin(), buffer->end(), true)
64 {
65 }
66 
67 Block::Block(ConstBufferPtr buffer, Buffer::const_iterator begin, Buffer::const_iterator end,
68  bool verifyLength)
69  : m_buffer(std::move(buffer))
70  , m_begin(begin)
71  , m_end(end)
73  , m_valueEnd(m_end)
74  , m_size(m_end - m_begin)
75 {
76  if (m_buffer->size() == 0) {
77  BOOST_THROW_EXCEPTION(std::invalid_argument("buffer is empty"));
78  }
79 
80  const uint8_t* bufferBegin = &m_buffer->front();
81  const uint8_t* bufferEnd = bufferBegin + m_buffer->size();
82  if (&*begin < bufferBegin || &*begin > bufferEnd ||
83  &*end < bufferBegin || &*end > bufferEnd) {
84  BOOST_THROW_EXCEPTION(std::invalid_argument("begin/end iterators points out of the buffer"));
85  }
86 
88  uint64_t length = tlv::readVarNumber(m_valueBegin, m_valueEnd);
89  // m_valueBegin now points to TLV-VALUE
90 
91  if (verifyLength && length != static_cast<uint64_t>(m_valueEnd - m_valueBegin)) {
92  BOOST_THROW_EXCEPTION(Error("TLV-LENGTH doesn't match buffer size"));
93  }
94 }
95 
96 Block::Block(const Block& block, Buffer::const_iterator begin, Buffer::const_iterator end,
97  bool verifyLength)
98  : Block(block.m_buffer, begin, end, verifyLength)
99 {
100 }
101 
103  Buffer::const_iterator begin, Buffer::const_iterator end,
104  Buffer::const_iterator valueBegin, Buffer::const_iterator valueEnd)
105  : m_buffer(std::move(buffer))
106  , m_begin(begin)
107  , m_end(end)
108  , m_valueBegin(valueBegin)
109  , m_valueEnd(valueEnd)
110  , m_type(type)
111  , m_size(m_end - m_begin)
112 {
113 }
114 
115 Block::Block(const uint8_t* buf, size_t bufSize)
116 {
117  const uint8_t* pos = buf;
118  const uint8_t* const end = buf + bufSize;
119 
120  m_type = tlv::readType(pos, end);
121  uint64_t length = tlv::readVarNumber(pos, end);
122  // pos now points to TLV-VALUE
123 
124  if (length > static_cast<uint64_t>(end - pos)) {
125  BOOST_THROW_EXCEPTION(Error("Not enough bytes in the buffer to fully parse TLV"));
126  }
127  size_t typeLengthSize = pos - buf;
128  m_size = typeLengthSize + length;
129 
130  m_buffer = make_shared<Buffer>(buf, m_size);
131  m_begin = m_buffer->begin();
132  m_end = m_valueEnd = m_buffer->end();
133  m_valueBegin = m_begin + typeLengthSize;
134 }
135 
137  : m_type(type)
138  , m_size(tlv::sizeOfVarNumber(m_type) + tlv::sizeOfVarNumber(0))
139 {
140 }
141 
143  : m_buffer(std::move(value))
144  , m_begin(m_buffer->end())
145  , m_end(m_buffer->end())
147  , m_valueEnd(m_buffer->end())
148  , m_type(type)
149 {
151 }
152 
153 Block::Block(uint32_t type, const Block& value)
154  : m_buffer(value.m_buffer)
155  , m_begin(m_buffer->end())
156  , m_end(m_buffer->end())
157  , m_valueBegin(value.begin())
158  , m_valueEnd(value.end())
159  , m_type(type)
160 {
162 }
163 
164 Block
165 Block::fromStream(std::istream& is)
166 {
167  std::istream_iterator<uint8_t> begin(is >> std::noskipws);
168  std::istream_iterator<uint8_t> end;
169 
170  uint32_t type = tlv::readType(begin, end);
171  uint64_t length = tlv::readVarNumber(begin, end);
172  if (begin != end) {
173  is.putback(*begin);
174  }
175 
176  size_t tlSize = tlv::sizeOfVarNumber(type) + tlv::sizeOfVarNumber(length);
177  if (tlSize + length > MAX_SIZE_OF_BLOCK_FROM_STREAM) {
178  BOOST_THROW_EXCEPTION(Error("TLV-LENGTH from stream exceeds limit"));
179  }
180 
181  EncodingBuffer eb(tlSize + length, length);
182  uint8_t* valueBuf = eb.buf();
183  is.read(reinterpret_cast<char*>(valueBuf), length);
184  if (length != static_cast<uint64_t>(is.gcount())) {
185  BOOST_THROW_EXCEPTION(Error("Not enough bytes from stream to fully parse TLV"));
186  }
187 
188  eb.prependVarNumber(length);
189  eb.prependVarNumber(type);
190 
191  // TLV-VALUE is directly written into eb.buf(), eb.end() is not incremented, but eb.getBuffer()
192  // has the correct layout.
193  return Block(eb.getBuffer());
194 }
195 
196 std::tuple<bool, Block>
197 Block::fromBuffer(ConstBufferPtr buffer, size_t offset)
198 {
199  const Buffer::const_iterator begin = buffer->begin() + offset;
200  Buffer::const_iterator pos = begin;
201 
202  uint32_t type = 0;
203  bool isOk = tlv::readType(pos, buffer->end(), type);
204  if (!isOk) {
205  return std::make_tuple(false, Block());
206  }
207  uint64_t length = 0;
208  isOk = tlv::readVarNumber(pos, buffer->end(), length);
209  if (!isOk) {
210  return std::make_tuple(false, Block());
211  }
212  // pos now points to TLV-VALUE
213 
214  if (length > static_cast<uint64_t>(buffer->end() - pos)) {
215  return std::make_tuple(false, Block());
216  }
217 
218  return std::make_tuple(true, Block(buffer, type, begin, pos + length, pos, pos + length));
219 }
220 
221 std::tuple<bool, Block>
222 Block::fromBuffer(const uint8_t* buf, size_t bufSize)
223 {
224  const uint8_t* pos = buf;
225  const uint8_t* const end = buf + bufSize;
226 
227  uint32_t type = 0;
228  bool isOk = tlv::readType(pos, end, type);
229  if (!isOk) {
230  return std::make_tuple(false, Block());
231  }
232  uint64_t length = 0;
233  isOk = tlv::readVarNumber(pos, end, length);
234  if (!isOk) {
235  return std::make_tuple(false, Block());
236  }
237  // pos now points to TLV-VALUE
238 
239  if (length > static_cast<uint64_t>(end - pos)) {
240  return std::make_tuple(false, Block());
241  }
242 
243  size_t typeLengthSize = pos - buf;
244  auto b = make_shared<Buffer>(buf, pos + length);
245  return std::make_tuple(true, Block(b, type, b->begin(), b->end(),
246  b->begin() + typeLengthSize, b->end()));
247 }
248 
249 // ---- wire format ----
250 
251 bool
253 {
254  return m_buffer != nullptr && m_begin != m_end;
255 }
256 
257 void
259 {
260  this->resetWire();
261 
262  m_type = std::numeric_limits<uint32_t>::max();
263  m_elements.clear();
264 }
265 
266 void
268 {
269  m_buffer.reset(); // discard underlying buffer by resetting shared_ptr
270  m_begin = m_end = m_valueBegin = m_valueEnd = Buffer::const_iterator();
271 }
272 
273 Buffer::const_iterator
275 {
276  if (!hasWire())
277  BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
278 
279  return m_begin;
280 }
281 
282 Buffer::const_iterator
283 Block::end() const
284 {
285  if (!hasWire())
286  BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
287 
288  return m_end;
289 }
290 
291 const uint8_t*
292 Block::wire() const
293 {
294  if (!hasWire())
295  BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
296 
297  return &*m_begin;
298 }
299 
300 size_t
301 Block::size() const
302 {
303  if (empty()) {
304  BOOST_THROW_EXCEPTION(Error("Block size cannot be determined (undefined block size)"));
305  }
306 
307  return m_size;
308 }
309 
310 // ---- value ----
311 
312 const uint8_t*
314 {
315  return hasValue() ? &*m_valueBegin : nullptr;
316 }
317 
318 size_t
320 {
321  return hasValue() ? m_valueEnd - m_valueBegin : 0;
322 }
323 
324 Block
326 {
327  if (!hasValue())
328  BOOST_THROW_EXCEPTION(Error("Block has no TLV-VALUE"));
329 
330  return Block(*this, m_valueBegin, m_valueEnd, true);
331 }
332 
333 // ---- sub elements ----
334 
335 void
337 {
338  if (!m_elements.empty() || value_size() == 0)
339  return;
340 
341  Buffer::const_iterator begin = value_begin();
342  Buffer::const_iterator end = value_end();
343 
344  while (begin != end) {
345  Buffer::const_iterator pos = begin;
346 
347  uint32_t type = tlv::readType(pos, end);
348  uint64_t length = tlv::readVarNumber(pos, end);
349  if (length > static_cast<uint64_t>(end - pos)) {
350  m_elements.clear();
351  BOOST_THROW_EXCEPTION(Error("TLV-LENGTH of sub-element of type " + to_string(type) +
352  " exceeds TLV-VALUE boundary of parent block"));
353  }
354  // pos now points to TLV-VALUE of sub element
355 
356  Buffer::const_iterator subEnd = pos + length;
357  m_elements.emplace_back(m_buffer, type, begin, subEnd, pos, subEnd);
358 
359  begin = subEnd;
360  }
361 }
362 
363 void
365 {
366  if (hasWire())
367  return;
368 
369  EncodingEstimator estimator;
370  size_t estimatedSize = encode(estimator);
371 
372  EncodingBuffer buffer(estimatedSize, 0);
373  encode(buffer);
374 }
375 
376 size_t
377 Block::encode(EncodingEstimator& estimator) const
378 {
379  if (hasValue()) {
380  return m_size;
381  }
382 
383  size_t len = encodeValue(estimator);
384  len += estimator.prependVarNumber(len);
385  len += estimator.prependVarNumber(m_type);
386  return len;
387 }
388 
389 size_t
390 Block::encodeValue(EncodingEstimator& estimator) const
391 {
392  size_t len = 0;
393  for (const Block& element : m_elements | boost::adaptors::reversed) {
394  len += element.encode(estimator);
395  }
396  return len;
397 }
398 
399 size_t
401 {
402  size_t len = 0;
403  m_end = encoder.begin();
404  if (hasValue()) {
405  len += encoder.prependRange(m_valueBegin, m_valueEnd);
406  }
407  else {
408  for (Block& element : m_elements | boost::adaptors::reversed) {
409  len += element.encode(encoder);
410  }
411  }
412  m_valueEnd = m_end;
413  m_valueBegin = encoder.begin();
414 
415  len += encoder.prependVarNumber(len);
416  len += encoder.prependVarNumber(m_type);
417  m_begin = encoder.begin();
418 
419  m_buffer = encoder.getBuffer();
420  m_size = len;
421  return len;
422 }
423 
424 const Block&
425 Block::get(uint32_t type) const
426 {
427  auto it = this->find(type);
428  if (it != m_elements.end()) {
429  return *it;
430  }
431 
432  BOOST_THROW_EXCEPTION(Error("No sub-element of type " + to_string(type) +
433  " is found in block of type " + to_string(m_type)));
434 }
435 
437 Block::find(uint32_t type) const
438 {
439  return std::find_if(m_elements.begin(), m_elements.end(),
440  [type] (const Block& subBlock) { return subBlock.type() == type; });
441 }
442 
443 void
445 {
446  resetWire();
447 
448  auto it = std::remove_if(m_elements.begin(), m_elements.end(),
449  [type] (const Block& subBlock) { return subBlock.type() == type; });
450  m_elements.resize(it - m_elements.begin());
451 }
452 
455 {
456  resetWire();
457 
458 #ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
459  return m_elements.erase(position);
460 #else
461  element_iterator it = m_elements.begin();
462  std::advance(it, std::distance(m_elements.cbegin(), position));
463  return m_elements.erase(it);
464 #endif
465 }
466 
469 {
470  resetWire();
471 
472 #ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
473  return m_elements.erase(first, last);
474 #else
475  element_iterator itStart = m_elements.begin();
476  element_iterator itEnd = m_elements.begin();
477  std::advance(itStart, std::distance(m_elements.cbegin(), first));
478  std::advance(itEnd, std::distance(m_elements.cbegin(), last));
479  return m_elements.erase(itStart, itEnd);
480 #endif
481 }
482 
483 void
484 Block::push_back(const Block& element)
485 {
486  resetWire();
487  m_elements.push_back(element);
488 }
489 
492 {
493  resetWire();
494 
495 #ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
496  return m_elements.insert(pos, element);
497 #else
498  element_iterator it = m_elements.begin();
499  std::advance(it, std::distance(m_elements.cbegin(), pos));
500  return m_elements.insert(it, element);
501 #endif
502 }
503 
504 // ---- misc ----
505 
506 Block::operator boost::asio::const_buffer() const
507 {
508  return boost::asio::const_buffer(wire(), size());
509 }
510 
511 bool
512 operator==(const Block& lhs, const Block& rhs)
513 {
514  return lhs.type() == rhs.type() &&
515  lhs.value_size() == rhs.value_size() &&
516  (lhs.value_size() == 0 ||
517  std::memcmp(lhs.value(), rhs.value(), lhs.value_size()) == 0);
518 }
519 
520 std::ostream&
521 operator<<(std::ostream& os, const Block& block)
522 {
523  auto oldFmt = os.flags(std::ios_base::dec);
524 
525  if (block.empty()) {
526  os << "[invalid]";
527  }
528  else if (!block.m_elements.empty()) {
529  EncodingEstimator estimator;
530  size_t tlvLength = block.encodeValue(estimator);
531  os << block.type() << '[' << tlvLength << "]={";
532  std::copy(block.elements_begin(), block.elements_end(), make_ostream_joiner(os, ','));
533  os << '}';
534  }
535  else if (block.value_size() > 0) {
536  os << block.type() << '[' << block.value_size() << "]=";
537  printHex(os, block.value(), block.value_size(), true);
538  }
539  else {
540  os << block.type() << "[empty]";
541  }
542 
543  os.flags(oldFmt);
544  return os;
545 }
546 
547 } // namespace ndn
shared_ptr< const Buffer > m_buffer
underlying buffer storing TLV-VALUE and possibly TLV-TYPE and TLV-LENGTH fields
Definition: block.hpp:408
bool empty() const
Check if the Block is empty.
Definition: block.hpp:173
static Block fromStream(std::istream &is)
Parse Block from an input stream.
Definition: block.cpp:165
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:66
bool hasValue() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:246
bool readType(Iterator &begin, const Iterator &end, uint32_t &type)
Read TLV-TYPE.
static std::tuple< bool, Block > fromBuffer(ConstBufferPtr buffer, size_t offset)
Try to parse Block from a wire buffer.
Definition: block.cpp:197
Buffer::const_iterator m_valueBegin
Definition: block.hpp:412
Buffer::const_iterator end() const
Get end iterator of encoded wire.
Definition: block.cpp:283
Buffer::const_iterator m_begin
Definition: block.hpp:409
element_const_iterator find(uint32_t type) const
Find the first sub element of specified TLV-TYPE.
Definition: block.cpp:437
element_container::const_iterator element_const_iterator
Definition: block.hpp:47
STL namespace.
element_container m_elements
sub elements
Definition: block.hpp:427
friend std::ostream & operator<<(std::ostream &os, const Block &block)
Print block to os.
Definition: block.cpp:521
Represents a TLV element of NDN packet format.
Definition: block.hpp:42
element_iterator insert(element_const_iterator pos, const Block &element)
Insert a sub element.
Definition: block.cpp:491
element_iterator erase(element_const_iterator position)
Erase a sub element.
Definition: block.cpp:454
Buffer::const_iterator value_begin() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:255
Buffer::const_iterator value_end() const
Get end iterator of TLV-VALUE.
Definition: block.hpp:264
void resetWire()
Reset wire buffer but keep TLV-TYPE and sub elements (if any)
Definition: block.cpp:267
ostream_joiner< typename std::decay< DelimT >::type, CharT, Traits > make_ostream_joiner(std::basic_ostream< CharT, Traits > &os, DelimT &&delimiter)
uint32_t m_size
total size including Type-Length-Value
Definition: block.hpp:421
bool readVarNumber(Iterator &begin, const Iterator &end, uint64_t &number)
Read VAR-NUMBER in NDN-TLV encoding.
Buffer::const_iterator m_valueEnd
Definition: block.hpp:413
size_t size() const
Get size of encoded wire, including Type-Length-Value.
Definition: block.cpp:301
Block blockFromValue() const
Definition: block.cpp:325
const Block & get(uint32_t type) const
Get the first sub element of specified TLV-TYPE.
Definition: block.cpp:425
element_container::iterator element_iterator
Definition: block.hpp:46
void remove(uint32_t type)
Remove all sub elements of specified TLV-TYPE.
Definition: block.cpp:444
void reset()
Reset wire buffer of the element.
Definition: block.cpp:258
void push_back(const Block &element)
Append a sub element.
Definition: block.cpp:484
size_t value_size() const
Get size of TLV-VALUE aka TLV-LENGTH.
Definition: block.cpp:319
void parse() const
Parse TLV-VALUE into sub elements.
Definition: block.cpp:336
uint32_t type() const
Get TLV-TYPE.
Definition: block.hpp:235
void encode()
Encode sub elements into TLV-VALUE.
Definition: block.cpp:364
const uint8_t * wire() const
Get pointer to encoded wire.
Definition: block.cpp:292
Buffer::const_iterator m_end
Definition: block.hpp:410
bool hasWire() const
Check if the Block has fully encoded wire.
Definition: block.cpp:252
const uint8_t * value() const
Get pointer to TLV-VALUE.
Definition: block.cpp:313
bool operator==(const Data &lhs, const Data &rhs)
Definition: data.cpp:328
shared_ptr< const Buffer > getBuffer() const
Get underlying buffer.
Definition: block.hpp:226
void printHex(std::ostream &os, uint64_t num, bool wantUpperCase)
Output the hex representation of num to the output stream os.
std::string to_string(const V &v)
Definition: backports.hpp:107
element_const_iterator elements_end() const
Equivalent to elements().end()
Definition: block.hpp:363
uint32_t m_type
TLV-TYPE.
Definition: block.hpp:415
constexpr size_t sizeOfVarNumber(uint64_t number)
Get number of bytes necessary to hold value of VAR-NUMBER.
element_const_iterator elements_begin() const
Equivalent to elements().begin()
Definition: block.hpp:355
Block()
Create an empty Block.
Definition: block.cpp:51
EncodingImpl< EncoderTag > EncodingBuffer
Buffer::const_iterator begin() const
Get begin iterator of encoded wire.
Definition: block.cpp:274
EncodingImpl< EstimatorTag > EncodingEstimator
const size_t MAX_NDN_PACKET_SIZE
practical limit of network layer packet size
const size_t MAX_SIZE_OF_BLOCK_FROM_STREAM
Definition: block.cpp:47
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:89