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 
114 Block::Block(const Block& block, Buffer::const_iterator begin, Buffer::const_iterator end,
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(const uint8_t* buf, size_t bufSize)
134  : Block(make_span(buf, bufSize))
135 {
136 }
137 
138 Block::Block(uint32_t type)
139  : m_type(type)
140  , m_size(tlv::sizeOfVarNumber(m_type) + tlv::sizeOfVarNumber(0))
141 {
142 }
143 
144 Block::Block(uint32_t type, ConstBufferPtr value)
145  : m_buffer(std::move(value))
146  , m_begin(m_buffer->end())
147  , m_end(m_buffer->end())
148  , m_valueBegin(m_buffer->begin())
149  , m_valueEnd(m_buffer->end())
150  , m_type(type)
151 {
153 }
154 
155 Block::Block(uint32_t type, const Block& value)
156  : m_buffer(value.m_buffer)
157  , m_begin(m_buffer->end())
158  , m_end(m_buffer->end())
159  , m_valueBegin(value.begin())
160  , m_valueEnd(value.end())
161  , m_type(type)
162 {
164 }
165 
166 std::tuple<bool, Block>
167 Block::fromBuffer(ConstBufferPtr buffer, size_t offset)
168 {
169  auto begin = std::next(buffer->begin(), offset);
170  auto pos = begin;
171  const auto end = buffer->end();
172 
173  uint32_t type = 0;
174  bool isOk = tlv::readType(pos, end, type);
175  if (!isOk) {
176  return std::make_tuple(false, Block());
177  }
178 
179  uint64_t length = 0;
180  isOk = tlv::readVarNumber(pos, end, length);
181  if (!isOk) {
182  return std::make_tuple(false, Block());
183  }
184  // pos now points to TLV-VALUE
185 
186  BOOST_ASSERT(pos <= end);
187  if (length > static_cast<uint64_t>(std::distance(pos, end))) {
188  return std::make_tuple(false, Block());
189  }
190 
191  return std::make_tuple(true, Block(std::move(buffer), type, begin, pos + length, pos, pos + length));
192 }
193 
194 std::tuple<bool, Block>
195 Block::fromBuffer(span<const uint8_t> buffer)
196 {
197  auto pos = buffer.begin();
198  const auto end = buffer.end();
199 
200  uint32_t type = 0;
201  bool isOk = tlv::readType(pos, end, type);
202  if (!isOk) {
203  return std::make_tuple(false, Block());
204  }
205  uint64_t length = 0;
206  isOk = tlv::readVarNumber(pos, end, length);
207  if (!isOk) {
208  return std::make_tuple(false, Block());
209  }
210  // pos now points to TLV-VALUE
211 
212  BOOST_ASSERT(pos <= end);
213  if (length > static_cast<uint64_t>(std::distance(pos, end))) {
214  return std::make_tuple(false, Block());
215  }
216  std::advance(pos, length);
217  // pos now points to the end of the TLV
218 
219  auto b = std::make_shared<Buffer>(buffer.begin(), pos);
220  return std::make_tuple(true, Block(b, type, b->begin(), b->end(),
221  std::prev(b->end(), length), b->end()));
222 }
223 
224 std::tuple<bool, Block>
225 Block::fromBuffer(const uint8_t* buf, size_t bufSize)
226 {
227  return fromBuffer({buf, bufSize});
228 }
229 
230 Block
231 Block::fromStream(std::istream& is)
232 {
233  std::istream_iterator<uint8_t> begin(is >> std::noskipws);
234  std::istream_iterator<uint8_t> end;
235 
236  uint32_t type = tlv::readType(begin, end);
237  uint64_t length = tlv::readVarNumber(begin, end);
238  if (begin != end) {
239  is.putback(*begin);
240  }
241 
242  size_t tlSize = tlv::sizeOfVarNumber(type) + tlv::sizeOfVarNumber(length);
243  if (tlSize + length > MAX_SIZE_OF_BLOCK_FROM_STREAM) {
244  NDN_THROW(Error("TLV-LENGTH from stream exceeds limit"));
245  }
246 
247  EncodingBuffer eb(tlSize + length, length);
248  uint8_t* valueBuf = eb.data();
249  is.read(reinterpret_cast<char*>(valueBuf), length);
250  if (length != static_cast<uint64_t>(is.gcount())) {
251  NDN_THROW(Error("Not enough bytes from stream to fully parse TLV"));
252  }
253 
254  eb.prependVarNumber(length);
255  eb.prependVarNumber(type);
256 
257  // TLV-VALUE is directly written into eb.buf(), eb.end() is not incremented, but eb.getBuffer()
258  // has the correct layout.
259  return Block(eb.getBuffer());
260 }
261 
262 // ---- wire format ----
263 
264 void
265 Block::reset() noexcept
266 {
267  *this = {};
268 }
269 
270 void
272 {
273  m_buffer.reset(); // discard underlying buffer by resetting shared_ptr
275 }
276 
277 Buffer::const_iterator
279 {
280  if (!hasWire())
281  NDN_THROW(Error("Underlying wire buffer is empty"));
282 
283  return m_begin;
284 }
285 
286 Buffer::const_iterator
287 Block::end() const
288 {
289  if (!hasWire())
290  NDN_THROW(Error("Underlying wire buffer is empty"));
291 
292  return m_end;
293 }
294 
295 const uint8_t*
296 Block::wire() const
297 {
298  if (!hasWire())
299  NDN_THROW(Error("Underlying wire buffer is empty"));
300 
301  return &*m_begin;
302 }
303 
304 size_t
305 Block::size() const
306 {
307  if (!isValid()) {
308  NDN_THROW(Error("Cannot determine size of invalid block"));
309  }
310 
311  return m_size;
312 }
313 
314 // ---- value ----
315 
316 const uint8_t*
317 Block::value() const noexcept
318 {
319  return value_size() > 0 ? &*m_valueBegin : nullptr;
320 }
321 
322 size_t
323 Block::value_size() const noexcept
324 {
325  return hasValue() ? static_cast<size_t>(m_valueEnd - m_valueBegin) : 0;
326 }
327 
328 Block
330 {
331  if (value_size() == 0) {
332  NDN_THROW(Error("Cannot construct block from empty TLV-VALUE"));
333  }
334 
335  return Block(*this, m_valueBegin, m_valueEnd, true);
336 }
337 
338 // ---- sub elements ----
339 
340 void
342 {
343  if (!m_elements.empty() || value_size() == 0)
344  return;
345 
346  auto begin = value_begin();
347  auto end = value_end();
348 
349  while (begin != end) {
350  auto pos = begin;
351  uint32_t type = tlv::readType(pos, end);
352  uint64_t length = tlv::readVarNumber(pos, end);
353  if (length > static_cast<uint64_t>(end - pos)) {
354  m_elements.clear();
355  NDN_THROW(Error("TLV-LENGTH of sub-element of type " + to_string(type) +
356  " exceeds TLV-VALUE boundary of parent block"));
357  }
358  // pos now points to TLV-VALUE of sub element
359 
360  auto subEnd = std::next(pos, length);
361  m_elements.emplace_back(m_buffer, type, begin, subEnd, pos, subEnd);
362 
363  begin = subEnd;
364  }
365 }
366 
367 void
369 {
370  if (hasWire())
371  return;
372 
373  EncodingEstimator estimator;
374  size_t estimatedSize = encode(estimator);
375 
376  EncodingBuffer buffer(estimatedSize, 0);
377  encode(buffer);
378 }
379 
380 size_t
381 Block::encode(EncodingEstimator& estimator) const
382 {
383  if (hasValue()) {
384  return m_size;
385  }
386 
387  size_t len = encodeValue(estimator);
388  len += estimator.prependVarNumber(len);
389  len += estimator.prependVarNumber(m_type);
390  return len;
391 }
392 
393 size_t
394 Block::encodeValue(EncodingEstimator& estimator) const
395 {
396  size_t len = 0;
397  for (const Block& element : m_elements | boost::adaptors::reversed) {
398  len += element.encode(estimator);
399  }
400  return len;
401 }
402 
403 size_t
405 {
406  size_t len = 0;
407  m_end = encoder.begin();
408  if (hasValue()) {
409  len += encoder.prependRange(m_valueBegin, m_valueEnd);
410  }
411  else {
412  for (Block& element : m_elements | boost::adaptors::reversed) {
413  len += element.encode(encoder);
414  }
415  }
416  m_valueEnd = m_end;
417  m_valueBegin = encoder.begin();
418 
419  len += encoder.prependVarNumber(len);
420  len += encoder.prependVarNumber(m_type);
421  m_begin = encoder.begin();
422 
423  m_buffer = encoder.getBuffer();
424  m_size = len;
425  return len;
426 }
427 
428 const Block&
429 Block::get(uint32_t type) const
430 {
431  auto it = this->find(type);
432  if (it != m_elements.end()) {
433  return *it;
434  }
435 
436  NDN_THROW(Error("No sub-element of type " + to_string(type) +
437  " found in block of type " + to_string(m_type)));
438 }
439 
441 Block::find(uint32_t type) const
442 {
443  return std::find_if(m_elements.begin(), m_elements.end(),
444  [type] (const Block& subBlock) { return subBlock.type() == type; });
445 }
446 
447 void
448 Block::remove(uint32_t type)
449 {
450  resetWire();
451 
452  auto it = std::remove_if(m_elements.begin(), m_elements.end(),
453  [type] (const Block& subBlock) { return subBlock.type() == type; });
454  m_elements.erase(it, m_elements.end());
455 }
456 
459 {
460  resetWire();
461  return m_elements.erase(position);
462 }
463 
466 {
467  resetWire();
468  return m_elements.erase(first, last);
469 }
470 
471 void
472 Block::push_back(const Block& element)
473 {
474  resetWire();
475  m_elements.push_back(element);
476 }
477 
480 {
481  resetWire();
482  return m_elements.insert(pos, element);
483 }
484 
485 // ---- misc ----
486 
487 Block::operator boost::asio::const_buffer() const
488 {
489  return {wire(), size()};
490 }
491 
492 bool
493 operator==(const Block& lhs, const Block& rhs)
494 {
495  return lhs.type() == rhs.type() &&
496  lhs.value_size() == rhs.value_size() &&
497  (lhs.value_size() == 0 ||
498  std::memcmp(lhs.value(), rhs.value(), lhs.value_size()) == 0);
499 }
500 
501 std::ostream&
502 operator<<(std::ostream& os, const Block& block)
503 {
504  auto oldFmt = os.flags(std::ios_base::dec);
505 
506  if (!block.isValid()) {
507  os << "[invalid]";
508  }
509  else if (!block.m_elements.empty()) {
510  EncodingEstimator estimator;
511  size_t tlvLength = block.encodeValue(estimator);
512  os << block.type() << '[' << tlvLength << "]={";
513  std::copy(block.elements_begin(), block.elements_end(), make_ostream_joiner(os, ','));
514  os << '}';
515  }
516  else if (block.value_size() > 0) {
517  os << block.type() << '[' << block.value_size() << "]=";
518  printHex(os, block.value(), block.value_size(), true);
519  }
520  else {
521  os << block.type() << "[empty]";
522  }
523 
524  os.flags(oldFmt);
525  return os;
526 }
527 
528 Block
529 operator "" _block(const char* input, std::size_t len)
530 {
531  namespace t = security::transform;
532  t::StepSource ss;
533  OBufferStream os;
534  ss >> t::hexDecode() >> t::streamSink(os);
535 
536  for (const char* end = input + len; input != end; ++input) {
537  if (std::strchr("0123456789ABCDEF", *input) != nullptr) {
538  ss.write({reinterpret_cast<const uint8_t*>(input), 1});
539  }
540  }
541 
542  try {
543  ss.end();
544  }
545  catch (const t::Error&) {
546  NDN_THROW(std::invalid_argument("Input has odd number of hexadecimal digits"));
547  }
548 
549  return Block(os.buf());
550 }
551 
552 } // namespace ndn
Represents a TLV element of the NDN packet format.
Definition: block.hpp:45
element_container::const_iterator element_const_iterator
Definition: block.hpp:49
uint32_t m_type
TLV-TYPE.
Definition: block.hpp:470
const uint8_t * wire() const
Return a raw pointer to the beginning of the encoded wire.
Definition: block.cpp:296
element_const_iterator find(uint32_t type) const
Find the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:441
uint32_t type() const
Return the TLV-TYPE of the Block.
Definition: block.hpp:285
Block blockFromValue() const
Definition: block.cpp:329
element_iterator erase(element_const_iterator position)
Erase a sub-element.
Definition: block.cpp:458
Buffer::const_iterator begin() const
Get begin iterator of encoded wire.
Definition: block.cpp:278
void remove(uint32_t type)
Remove all sub-elements of the specified TLV-TYPE.
Definition: block.cpp:448
size_t size() const
Return the size of the encoded wire, i.e.
Definition: block.cpp:305
bool hasWire() const noexcept
Check if the Block contains a fully encoded wire representation.
Definition: block.hpp:241
Buffer::const_iterator end() const
Get end iterator of encoded wire.
Definition: block.cpp:287
size_t m_size
Total size including Type-Length-Value.
Definition: block.hpp:476
Buffer::const_iterator m_valueEnd
Definition: block.hpp:468
void resetWire() noexcept
Reset wire buffer but keep TLV-TYPE and sub-elements (if any)
Definition: block.cpp:271
element_container m_elements
Contains the sub-elements.
Definition: block.hpp:482
static std::tuple< bool, Block > fromBuffer(ConstBufferPtr buffer, size_t offset=0)
Try to parse Block from a wire buffer.
Definition: block.cpp:167
void push_back(const Block &element)
Append a sub-element.
Definition: block.cpp:472
Block & operator=(const Block &)
Copy assignment operator.
Buffer::const_iterator m_end
Definition: block.hpp:465
Buffer::const_iterator m_valueBegin
Definition: block.hpp:467
Buffer::const_iterator value_end() const
Get end iterator of TLV-VALUE.
Definition: block.hpp:316
bool hasValue() const noexcept
Check if the Block has a non-empty TLV-VALUE.
Definition: block.hpp:298
bool isValid() const noexcept
Check if the Block is valid.
Definition: block.hpp:212
element_container::iterator element_iterator
Definition: block.hpp:48
void encode()
Encode sub-elements into TLV-VALUE.
Definition: block.cpp:368
Buffer::const_iterator value_begin() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:307
element_const_iterator elements_end() const
Equivalent to elements().end()
Definition: block.hpp:418
element_const_iterator elements_begin() const
Equivalent to elements().begin()
Definition: block.hpp:410
static Block fromStream(std::istream &is)
Parse Block from an input stream.
Definition: block.cpp:231
void reset() noexcept
Reset the Block to a default-constructed state.
Definition: block.cpp:265
Buffer::const_iterator m_begin
Definition: block.hpp:464
shared_ptr< const Buffer > m_buffer
Underlying buffer storing TLV-VALUE and possibly TLV-TYPE and TLV-LENGTH fields.
Definition: block.hpp:463
void parse() const
Parse TLV-VALUE into sub-elements.
Definition: block.cpp:341
const Block & get(uint32_t type) const
Return the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:429
size_t value_size() const noexcept
Return the size of TLV-VALUE, aka TLV-LENGTH.
Definition: block.cpp:323
Block()
Create an invalid Block.
element_iterator insert(element_const_iterator pos, const Block &element)
Insert a sub-element.
Definition: block.cpp:479
const uint8_t * value() const noexcept
Return a raw pointer to the beginning of TLV-VALUE.
Definition: block.cpp:317
implements an output stream that constructs ndn::Buffer
shared_ptr< Buffer > buf()
Flush written data to the stream and return 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:454
bool readVarNumber(Iterator &begin, Iterator end, uint64_t &number) noexcept
Read VAR-NUMBER in NDN-TLV encoding.
Definition: tlv.hpp:393
bool readType(Iterator &begin, Iterator end, uint32_t &type) noexcept
Read TLV-TYPE.
Definition: tlv.hpp:412
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:366
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:376
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.