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