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 
27 #include "ndn-cxx/encoding/tlv.hpp"
30 
31 #include <boost/asio/buffer.hpp>
32 #include <boost/range/adaptor/reversed.hpp>
33 #include <cstring>
34 
35 namespace ndn {
36 
37 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Block>));
38 
40 
41 // ---- constructor, creation, assignment ----
42 
43 Block::Block() = default;
44 
45 Block::Block(const Block&) = default;
46 
47 Block&
48 Block::operator=(const Block&) = default;
49 
51  : Block(buffer.getBuffer(), buffer.begin(), buffer.end(), true)
52 {
53 }
54 
56  : Block(buffer, buffer->begin(), buffer->end(), true)
57 {
58 }
59 
60 Block::Block(ConstBufferPtr buffer, Buffer::const_iterator begin, Buffer::const_iterator end,
61  bool verifyLength)
62  : m_buffer(std::move(buffer))
63  , m_begin(begin)
64  , m_end(end)
66  , m_valueEnd(m_end)
67  , m_size(m_end - m_begin)
68 {
69  if (m_buffer->size() == 0) {
70  BOOST_THROW_EXCEPTION(std::invalid_argument("buffer is empty"));
71  }
72 
73  const uint8_t* bufferBegin = &m_buffer->front();
74  const uint8_t* bufferEnd = bufferBegin + m_buffer->size();
75  if (&*begin < bufferBegin || &*begin > bufferEnd ||
76  &*end < bufferBegin || &*end > bufferEnd) {
77  BOOST_THROW_EXCEPTION(std::invalid_argument("begin/end iterators points out of the buffer"));
78  }
79 
81  uint64_t length = tlv::readVarNumber(m_valueBegin, m_valueEnd);
82  // m_valueBegin now points to TLV-VALUE
83 
84  if (verifyLength && length != static_cast<uint64_t>(m_valueEnd - m_valueBegin)) {
85  BOOST_THROW_EXCEPTION(Error("TLV-LENGTH doesn't match buffer size"));
86  }
87 }
88 
89 Block::Block(const Block& block, Buffer::const_iterator begin, Buffer::const_iterator end,
90  bool verifyLength)
91  : Block(block.m_buffer, begin, end, verifyLength)
92 {
93 }
94 
95 Block::Block(ConstBufferPtr buffer, uint32_t type,
96  Buffer::const_iterator begin, Buffer::const_iterator end,
97  Buffer::const_iterator valueBegin, Buffer::const_iterator valueEnd)
98  : m_buffer(std::move(buffer))
99  , m_begin(begin)
100  , m_end(end)
101  , m_valueBegin(valueBegin)
102  , m_valueEnd(valueEnd)
103  , m_type(type)
104  , m_size(m_end - m_begin)
105 {
106 }
107 
108 Block::Block(const uint8_t* buf, size_t bufSize)
109 {
110  const uint8_t* pos = buf;
111  const uint8_t* const end = buf + bufSize;
112 
113  m_type = tlv::readType(pos, end);
114  uint64_t length = tlv::readVarNumber(pos, end);
115  // pos now points to TLV-VALUE
116 
117  BOOST_ASSERT(pos <= end);
118  if (length > static_cast<uint64_t>(end - pos)) {
119  BOOST_THROW_EXCEPTION(Error("Not enough bytes in the buffer to fully parse TLV"));
120  }
121 
122  BOOST_ASSERT(pos > buf);
123  uint64_t typeLengthSize = static_cast<uint64_t>(pos - buf);
124  m_size = typeLengthSize + length;
125 
126  m_buffer = make_shared<Buffer>(buf, m_size);
127  m_begin = m_buffer->begin();
128  m_end = m_valueEnd = m_buffer->end();
129  m_valueBegin = m_begin + typeLengthSize;
130 }
131 
133  : m_type(type)
134  , m_size(tlv::sizeOfVarNumber(m_type) + tlv::sizeOfVarNumber(0))
135 {
136 }
137 
139  : m_buffer(std::move(value))
140  , m_begin(m_buffer->end())
141  , m_end(m_buffer->end())
143  , m_valueEnd(m_buffer->end())
144  , m_type(type)
145 {
147 }
148 
149 Block::Block(uint32_t type, const Block& value)
150  : m_buffer(value.m_buffer)
151  , m_begin(m_buffer->end())
152  , m_end(m_buffer->end())
153  , m_valueBegin(value.begin())
154  , m_valueEnd(value.end())
155  , m_type(type)
156 {
158 }
159 
160 Block
161 Block::fromStream(std::istream& is)
162 {
163  std::istream_iterator<uint8_t> begin(is >> std::noskipws);
164  std::istream_iterator<uint8_t> end;
165 
166  uint32_t type = tlv::readType(begin, end);
167  uint64_t length = tlv::readVarNumber(begin, end);
168  if (begin != end) {
169  is.putback(*begin);
170  }
171 
172  size_t tlSize = tlv::sizeOfVarNumber(type) + tlv::sizeOfVarNumber(length);
173  if (tlSize + length > MAX_SIZE_OF_BLOCK_FROM_STREAM) {
174  BOOST_THROW_EXCEPTION(Error("TLV-LENGTH from stream exceeds limit"));
175  }
176 
177  EncodingBuffer eb(tlSize + length, length);
178  uint8_t* valueBuf = eb.buf();
179  is.read(reinterpret_cast<char*>(valueBuf), length);
180  if (length != static_cast<uint64_t>(is.gcount())) {
181  BOOST_THROW_EXCEPTION(Error("Not enough bytes from stream to fully parse TLV"));
182  }
183 
184  eb.prependVarNumber(length);
185  eb.prependVarNumber(type);
186 
187  // TLV-VALUE is directly written into eb.buf(), eb.end() is not incremented, but eb.getBuffer()
188  // has the correct layout.
189  return Block(eb.getBuffer());
190 }
191 
192 std::tuple<bool, Block>
193 Block::fromBuffer(ConstBufferPtr buffer, size_t offset)
194 {
195  auto begin = buffer->begin() + offset;
196  auto pos = begin;
197 
198  uint32_t type = 0;
199  bool isOk = tlv::readType(pos, buffer->end(), type);
200  if (!isOk) {
201  return std::make_tuple(false, Block());
202  }
203 
204  uint64_t length = 0;
205  isOk = tlv::readVarNumber(pos, buffer->end(), length);
206  if (!isOk) {
207  return std::make_tuple(false, Block());
208  }
209  // pos now points to TLV-VALUE
210 
211  if (length > static_cast<uint64_t>(buffer->end() - pos)) {
212  return std::make_tuple(false, Block());
213  }
214 
215  return std::make_tuple(true, Block(std::move(buffer), type, begin, pos + length, pos, pos + length));
216 }
217 
218 std::tuple<bool, Block>
219 Block::fromBuffer(const uint8_t* buf, size_t bufSize)
220 {
221  const uint8_t* pos = buf;
222  const uint8_t* const end = buf + bufSize;
223 
224  uint32_t type = 0;
225  bool isOk = tlv::readType(pos, end, type);
226  if (!isOk) {
227  return std::make_tuple(false, Block());
228  }
229  uint64_t length = 0;
230  isOk = tlv::readVarNumber(pos, end, length);
231  if (!isOk) {
232  return std::make_tuple(false, Block());
233  }
234  // pos now points to TLV-VALUE
235 
236  if (length > static_cast<uint64_t>(end - pos)) {
237  return std::make_tuple(false, Block());
238  }
239 
240  size_t typeLengthSize = pos - buf;
241  auto b = make_shared<Buffer>(buf, pos + length);
242  return std::make_tuple(true, Block(b, type, b->begin(), b->end(),
243  b->begin() + typeLengthSize, b->end()));
244 }
245 
246 // ---- wire format ----
247 
248 bool
250 {
251  return m_buffer != nullptr && m_begin != m_end;
252 }
253 
254 void
256 {
257  this->resetWire();
258 
259  m_type = std::numeric_limits<uint32_t>::max();
260  m_elements.clear();
261 }
262 
263 void
265 {
266  m_buffer.reset(); // discard underlying buffer by resetting shared_ptr
268 }
269 
270 Buffer::const_iterator
272 {
273  if (!hasWire())
274  BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
275 
276  return m_begin;
277 }
278 
279 Buffer::const_iterator
280 Block::end() const
281 {
282  if (!hasWire())
283  BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
284 
285  return m_end;
286 }
287 
288 const uint8_t*
289 Block::wire() const
290 {
291  if (!hasWire())
292  BOOST_THROW_EXCEPTION(Error("Underlying wire buffer is empty"));
293 
294  return &*m_begin;
295 }
296 
297 size_t
298 Block::size() const
299 {
300  if (empty()) {
301  BOOST_THROW_EXCEPTION(Error("Block size cannot be determined (undefined block size)"));
302  }
303 
304  return m_size;
305 }
306 
307 // ---- value ----
308 
309 const uint8_t*
311 {
312  return hasValue() ? &*m_valueBegin : nullptr;
313 }
314 
315 size_t
317 {
318  return hasValue() ? static_cast<size_t>(m_valueEnd - m_valueBegin) : 0;
319 }
320 
321 Block
323 {
324  if (!hasValue())
325  BOOST_THROW_EXCEPTION(Error("Block has no TLV-VALUE"));
326 
327  return Block(*this, m_valueBegin, m_valueEnd, true);
328 }
329 
330 // ---- sub elements ----
331 
332 void
334 {
335  if (!m_elements.empty() || value_size() == 0)
336  return;
337 
338  Buffer::const_iterator begin = value_begin();
339  Buffer::const_iterator end = value_end();
340 
341  while (begin != end) {
342  Buffer::const_iterator pos = begin;
343 
344  uint32_t type = tlv::readType(pos, end);
345  uint64_t length = tlv::readVarNumber(pos, end);
346  if (length > static_cast<uint64_t>(end - pos)) {
347  m_elements.clear();
348  BOOST_THROW_EXCEPTION(Error("TLV-LENGTH of sub-element of type " + to_string(type) +
349  " exceeds TLV-VALUE boundary of parent block"));
350  }
351  // pos now points to TLV-VALUE of sub element
352 
353  Buffer::const_iterator subEnd = pos + length;
354  m_elements.emplace_back(m_buffer, type, begin, subEnd, pos, subEnd);
355 
356  begin = subEnd;
357  }
358 }
359 
360 void
362 {
363  if (hasWire())
364  return;
365 
366  EncodingEstimator estimator;
367  size_t estimatedSize = encode(estimator);
368 
369  EncodingBuffer buffer(estimatedSize, 0);
370  encode(buffer);
371 }
372 
373 size_t
374 Block::encode(EncodingEstimator& estimator) const
375 {
376  if (hasValue()) {
377  return m_size;
378  }
379 
380  size_t len = encodeValue(estimator);
381  len += estimator.prependVarNumber(len);
382  len += estimator.prependVarNumber(m_type);
383  return len;
384 }
385 
386 size_t
387 Block::encodeValue(EncodingEstimator& estimator) const
388 {
389  size_t len = 0;
390  for (const Block& element : m_elements | boost::adaptors::reversed) {
391  len += element.encode(estimator);
392  }
393  return len;
394 }
395 
396 size_t
398 {
399  size_t len = 0;
400  m_end = encoder.begin();
401  if (hasValue()) {
402  len += encoder.prependRange(m_valueBegin, m_valueEnd);
403  }
404  else {
405  for (Block& element : m_elements | boost::adaptors::reversed) {
406  len += element.encode(encoder);
407  }
408  }
409  m_valueEnd = m_end;
410  m_valueBegin = encoder.begin();
411 
412  len += encoder.prependVarNumber(len);
413  len += encoder.prependVarNumber(m_type);
414  m_begin = encoder.begin();
415 
416  m_buffer = encoder.getBuffer();
417  m_size = len;
418  return len;
419 }
420 
421 const Block&
422 Block::get(uint32_t type) const
423 {
424  auto it = this->find(type);
425  if (it != m_elements.end()) {
426  return *it;
427  }
428 
429  BOOST_THROW_EXCEPTION(Error("No sub-element of type " + to_string(type) +
430  " is found in block of type " + to_string(m_type)));
431 }
432 
434 Block::find(uint32_t type) const
435 {
436  return std::find_if(m_elements.begin(), m_elements.end(),
437  [type] (const Block& subBlock) { return subBlock.type() == type; });
438 }
439 
440 void
442 {
443  resetWire();
444 
445  auto it = std::remove_if(m_elements.begin(), m_elements.end(),
446  [type] (const Block& subBlock) { return subBlock.type() == type; });
447  m_elements.resize(it - m_elements.begin());
448 }
449 
452 {
453  resetWire();
454  return m_elements.erase(position);
455 }
456 
459 {
460  resetWire();
461  return m_elements.erase(first, last);
462 }
463 
464 void
465 Block::push_back(const Block& element)
466 {
467  resetWire();
468  m_elements.push_back(element);
469 }
470 
473 {
474  resetWire();
475  return m_elements.insert(pos, element);
476 }
477 
478 // ---- misc ----
479 
480 Block::operator boost::asio::const_buffer() const
481 {
482  return boost::asio::const_buffer(wire(), size());
483 }
484 
485 bool
486 operator==(const Block& lhs, const Block& rhs)
487 {
488  return lhs.type() == rhs.type() &&
489  lhs.value_size() == rhs.value_size() &&
490  (lhs.value_size() == 0 ||
491  std::memcmp(lhs.value(), rhs.value(), lhs.value_size()) == 0);
492 }
493 
494 std::ostream&
495 operator<<(std::ostream& os, const Block& block)
496 {
497  auto oldFmt = os.flags(std::ios_base::dec);
498 
499  if (block.empty()) {
500  os << "[invalid]";
501  }
502  else if (!block.m_elements.empty()) {
503  EncodingEstimator estimator;
504  size_t tlvLength = block.encodeValue(estimator);
505  os << block.type() << '[' << tlvLength << "]={";
506  std::copy(block.elements_begin(), block.elements_end(), make_ostream_joiner(os, ','));
507  os << '}';
508  }
509  else if (block.value_size() > 0) {
510  os << block.type() << '[' << block.value_size() << "]=";
511  printHex(os, block.value(), block.value_size(), true);
512  }
513  else {
514  os << block.type() << "[empty]";
515  }
516 
517  os.flags(oldFmt);
518  return os;
519 }
520 
521 Block
522 operator "" _block(const char* input, std::size_t len)
523 {
524  namespace t = security::transform;
525  t::StepSource ss;
526  OBufferStream os;
527  ss >> t::hexDecode() >> t::streamSink(os);
528 
529  for (const char* end = input + len; input != end; ++input) {
530  if (std::strchr("0123456789ABCDEF", *input) != nullptr) {
531  ss.write(reinterpret_cast<const uint8_t*>(input), 1);
532  }
533  }
534 
535  try {
536  ss.end();
537  }
538  catch (const t::Error&) {
539  BOOST_THROW_EXCEPTION(std::invalid_argument("input has odd number of hexadecimal digits"));
540  }
541 
542  return Block(os.buf());
543 }
544 
545 } // namespace ndn
size_t m_size
total size including Type-Length-Value
Definition: block.hpp:435
shared_ptr< const Buffer > m_buffer
underlying buffer storing TLV-VALUE and possibly TLV-TYPE and TLV-LENGTH fields
Definition: block.hpp:422
bool empty() const
Check if the Block is empty.
Definition: block.hpp:187
static Block fromStream(std::istream &is)
Parse Block from an input stream.
Definition: block.cpp:161
Definition: data.cpp:26
bool hasValue() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:260
static std::tuple< bool, Block > fromBuffer(ConstBufferPtr buffer, size_t offset)
Try to parse Block from a wire buffer.
Definition: block.cpp:193
Buffer::const_iterator m_valueBegin
Definition: block.hpp:426
Buffer::const_iterator end() const
Get end iterator of encoded wire.
Definition: block.cpp:280
Buffer::const_iterator m_begin
Definition: block.hpp:423
element_const_iterator find(uint32_t type) const
Find the first sub element of specified TLV-TYPE.
Definition: block.cpp:434
element_container::const_iterator element_const_iterator
Definition: block.hpp:47
STL namespace.
element_container m_elements
sub elements
Definition: block.hpp:441
friend std::ostream & operator<<(std::ostream &os, const Block &block)
Print block to os.
Definition: block.cpp:495
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:472
element_iterator erase(element_const_iterator position)
Erase a sub element.
Definition: block.cpp:451
Buffer::const_iterator value_begin() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:269
Buffer::const_iterator value_end() const
Get end iterator of TLV-VALUE.
Definition: block.hpp:278
void resetWire()
Reset wire buffer but keep TLV-TYPE and sub elements (if any)
Definition: block.cpp:264
Buffer::const_iterator m_valueEnd
Definition: block.hpp:427
size_t size() const
Get size of encoded wire, including Type-Length-Value.
Definition: block.cpp:298
constexpr size_t sizeOfVarNumber(uint64_t number) noexcept
Get the number of bytes necessary to hold the value of number encoded as VAR-NUMBER.
Definition: tlv.hpp:448
Block blockFromValue() const
Definition: block.cpp:322
bool readType(Iterator &begin, Iterator end, uint32_t &type) noexcept
Read TLV-TYPE.
Definition: tlv.hpp:406
Block & operator=(const Block &)
Copy assignment operator.
unique_ptr< Sink > streamSink(std::ostream &os)
Definition: stream-sink.cpp:53
ostream_joiner< std::decay_t< DelimT >, CharT, Traits > make_ostream_joiner(std::basic_ostream< CharT, Traits > &os, DelimT &&delimiter)
const Block & get(uint32_t type) const
Get the first sub element of specified TLV-TYPE.
Definition: block.cpp:422
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:441
void reset()
Reset wire buffer of the element.
Definition: block.cpp:255
void push_back(const Block &element)
Append a sub element.
Definition: block.cpp:465
size_t value_size() const
Get size of TLV-VALUE aka TLV-LENGTH.
Definition: block.cpp:316
void parse() const
Parse TLV-VALUE into sub elements.
Definition: block.cpp:333
uint32_t type() const
Get TLV-TYPE.
Definition: block.hpp:249
unique_ptr< Transform > hexDecode()
Definition: hex-decode.cpp:114
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
void encode()
Encode sub elements into TLV-VALUE.
Definition: block.cpp:361
const uint8_t * wire() const
Get pointer to encoded wire.
Definition: block.cpp:289
Buffer::const_iterator m_end
Definition: block.hpp:424
bool hasWire() const
Check if the Block has fully encoded wire.
Definition: block.cpp:249
const uint8_t * value() const
Get pointer to TLV-VALUE.
Definition: block.cpp:310
bool operator==(const Data &lhs, const Data &rhs)
Definition: data.cpp:313
void printHex(std::ostream &os, uint64_t num, bool wantUpperCase)
Output the hex representation of num to the output stream os.
implements an output stream that constructs ndn::Buffer
std::string to_string(const V &v)
Definition: backports.hpp:67
element_const_iterator elements_end() const
Equivalent to elements().end()
Definition: block.hpp:377
uint32_t m_type
TLV-TYPE.
Definition: block.hpp:429
element_const_iterator elements_begin() const
Equivalent to elements().begin()
Definition: block.hpp:369
Block()
Create an empty Block.
EncodingImpl< EncoderTag > EncodingBuffer
Buffer::const_iterator begin() const
Get begin iterator of encoded wire.
Definition: block.cpp:271
EncodingImpl< EstimatorTag > EncodingEstimator
bool readVarNumber(Iterator &begin, Iterator end, uint64_t &number) noexcept
Read VAR-NUMBER in NDN-TLV encoding.
Definition: tlv.hpp:387
const size_t MAX_NDN_PACKET_SIZE
practical limit of network layer packet size
Definition: tlv.hpp:41
const size_t MAX_SIZE_OF_BLOCK_FROM_STREAM
Definition: block.cpp:39
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:126