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