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-2019 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  NDN_THROW(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  NDN_THROW(std::invalid_argument("Begin/end iterators point outside 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  NDN_THROW(Error("TLV-LENGTH does not 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  NDN_THROW(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  NDN_THROW(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  NDN_THROW(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 void
249 Block::reset() noexcept
250 {
251  *this = {};
252 }
253 
254 void
256 {
257  m_buffer.reset(); // discard underlying buffer by resetting shared_ptr
259 }
260 
261 Buffer::const_iterator
263 {
264  if (!hasWire())
265  NDN_THROW(Error("Underlying wire buffer is empty"));
266 
267  return m_begin;
268 }
269 
270 Buffer::const_iterator
271 Block::end() const
272 {
273  if (!hasWire())
274  NDN_THROW(Error("Underlying wire buffer is empty"));
275 
276  return m_end;
277 }
278 
279 const uint8_t*
280 Block::wire() const
281 {
282  if (!hasWire())
283  NDN_THROW(Error("Underlying wire buffer is empty"));
284 
285  return &*m_begin;
286 }
287 
288 size_t
289 Block::size() const
290 {
291  if (!isValid()) {
292  NDN_THROW(Error("Cannot determine size of invalid block"));
293  }
294 
295  return m_size;
296 }
297 
298 // ---- value ----
299 
300 const uint8_t*
301 Block::value() const noexcept
302 {
303  return hasValue() ? &*m_valueBegin : nullptr;
304 }
305 
306 size_t
307 Block::value_size() const noexcept
308 {
309  return hasValue() ? static_cast<size_t>(m_valueEnd - m_valueBegin) : 0;
310 }
311 
312 Block
314 {
315  if (!hasValue())
316  NDN_THROW(Error("Block has no TLV-VALUE"));
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  Buffer::const_iterator begin = value_begin();
330  Buffer::const_iterator end = value_end();
331 
332  while (begin != end) {
333  Buffer::const_iterator pos = begin;
334 
335  uint32_t type = tlv::readType(pos, end);
336  uint64_t length = tlv::readVarNumber(pos, end);
337  if (length > static_cast<uint64_t>(end - pos)) {
338  m_elements.clear();
339  NDN_THROW(Error("TLV-LENGTH of sub-element of type " + to_string(type) +
340  " exceeds TLV-VALUE boundary of parent block"));
341  }
342  // pos now points to TLV-VALUE of sub element
343 
344  Buffer::const_iterator subEnd = pos + length;
345  m_elements.emplace_back(m_buffer, type, begin, subEnd, pos, subEnd);
346 
347  begin = subEnd;
348  }
349 }
350 
351 void
353 {
354  if (hasWire())
355  return;
356 
357  EncodingEstimator estimator;
358  size_t estimatedSize = encode(estimator);
359 
360  EncodingBuffer buffer(estimatedSize, 0);
361  encode(buffer);
362 }
363 
364 size_t
365 Block::encode(EncodingEstimator& estimator) const
366 {
367  if (hasValue()) {
368  return m_size;
369  }
370 
371  size_t len = encodeValue(estimator);
372  len += estimator.prependVarNumber(len);
373  len += estimator.prependVarNumber(m_type);
374  return len;
375 }
376 
377 size_t
378 Block::encodeValue(EncodingEstimator& estimator) const
379 {
380  size_t len = 0;
381  for (const Block& element : m_elements | boost::adaptors::reversed) {
382  len += element.encode(estimator);
383  }
384  return len;
385 }
386 
387 size_t
389 {
390  size_t len = 0;
391  m_end = encoder.begin();
392  if (hasValue()) {
393  len += encoder.prependRange(m_valueBegin, m_valueEnd);
394  }
395  else {
396  for (Block& element : m_elements | boost::adaptors::reversed) {
397  len += element.encode(encoder);
398  }
399  }
400  m_valueEnd = m_end;
401  m_valueBegin = encoder.begin();
402 
403  len += encoder.prependVarNumber(len);
404  len += encoder.prependVarNumber(m_type);
405  m_begin = encoder.begin();
406 
407  m_buffer = encoder.getBuffer();
408  m_size = len;
409  return len;
410 }
411 
412 const Block&
413 Block::get(uint32_t type) const
414 {
415  auto it = this->find(type);
416  if (it != m_elements.end()) {
417  return *it;
418  }
419 
420  NDN_THROW(Error("No sub-element of type " + to_string(type) +
421  " found in block of type " + 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
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 
464 {
465  resetWire();
466  return m_elements.insert(pos, element);
467 }
468 
469 // ---- misc ----
470 
471 Block::operator boost::asio::const_buffer() const
472 {
473  return boost::asio::const_buffer(wire(), size());
474 }
475 
476 bool
477 operator==(const Block& lhs, const Block& rhs)
478 {
479  return lhs.type() == rhs.type() &&
480  lhs.value_size() == rhs.value_size() &&
481  (lhs.value_size() == 0 ||
482  std::memcmp(lhs.value(), rhs.value(), lhs.value_size()) == 0);
483 }
484 
485 std::ostream&
486 operator<<(std::ostream& os, const Block& block)
487 {
488  auto oldFmt = os.flags(std::ios_base::dec);
489 
490  if (!block.isValid()) {
491  os << "[invalid]";
492  }
493  else if (!block.m_elements.empty()) {
494  EncodingEstimator estimator;
495  size_t tlvLength = block.encodeValue(estimator);
496  os << block.type() << '[' << tlvLength << "]={";
497  std::copy(block.elements_begin(), block.elements_end(), make_ostream_joiner(os, ','));
498  os << '}';
499  }
500  else if (block.value_size() > 0) {
501  os << block.type() << '[' << block.value_size() << "]=";
502  printHex(os, block.value(), block.value_size(), true);
503  }
504  else {
505  os << block.type() << "[empty]";
506  }
507 
508  os.flags(oldFmt);
509  return os;
510 }
511 
512 Block
513 operator "" _block(const char* input, std::size_t len)
514 {
515  namespace t = security::transform;
516  t::StepSource ss;
517  OBufferStream os;
518  ss >> t::hexDecode() >> t::streamSink(os);
519 
520  for (const char* end = input + len; input != end; ++input) {
521  if (std::strchr("0123456789ABCDEF", *input) != nullptr) {
522  ss.write(reinterpret_cast<const uint8_t*>(input), 1);
523  }
524  }
525 
526  try {
527  ss.end();
528  }
529  catch (const t::Error&) {
530  NDN_THROW(std::invalid_argument("Input has odd number of hexadecimal digits"));
531  }
532 
533  return Block(os.buf());
534 }
535 
536 } // namespace ndn
size_t m_size
Total size including Type-Length-Value.
Definition: block.hpp:465
shared_ptr< const Buffer > m_buffer
Underlying buffer storing TLV-VALUE and possibly TLV-TYPE and TLV-LENGTH fields.
Definition: block.hpp:452
static Block fromStream(std::istream &is)
Parse Block from an input stream.
Definition: block.cpp:161
Definition: data.cpp:26
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:456
Buffer::const_iterator end() const
Get end iterator of encoded wire.
Definition: block.cpp:271
Buffer::const_iterator m_begin
Definition: block.hpp:453
element_const_iterator find(uint32_t type) const
Find the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:425
element_container::const_iterator element_const_iterator
Definition: block.hpp:47
STL namespace.
size_t value_size() const noexcept
Return the size of TLV-VALUE, aka TLV-LENGTH.
Definition: block.cpp:307
element_container m_elements
Contains the sub-elements.
Definition: block.hpp:471
friend std::ostream & operator<<(std::ostream &os, const Block &block)
Print block to os.
Definition: block.cpp:486
Represents a TLV element of NDN packet format.
Definition: block.hpp:42
void resetWire() noexcept
Reset wire buffer but keep TLV-TYPE and sub-elements (if any)
Definition: block.cpp:255
bool hasWire() const noexcept
Check if the Block contains a fully encoded wire representation.
Definition: block.hpp:230
element_iterator insert(element_const_iterator pos, const Block &element)
Insert a sub-element.
Definition: block.cpp:463
element_iterator erase(element_const_iterator position)
Erase a sub-element.
Definition: block.cpp:442
Buffer::const_iterator value_begin() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:296
Buffer::const_iterator value_end() const
Get end iterator of TLV-VALUE.
Definition: block.hpp:305
#define NDN_THROW(e)
Definition: exception.hpp:61
Buffer::const_iterator m_valueEnd
Definition: block.hpp:457
size_t size() const
Return the size of the encoded wire, i.e.
Definition: block.cpp:289
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:451
Block blockFromValue() const
Definition: block.cpp:313
void reset() noexcept
Reset the Block to a default-constructed state.
Definition: block.cpp:249
bool readType(Iterator &begin, Iterator end, uint32_t &type) noexcept
Read TLV-TYPE.
Definition: tlv.hpp:409
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
Return the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:413
element_container::iterator element_iterator
Definition: block.hpp:46
void remove(uint32_t type)
Remove all sub-elements of the specified TLV-TYPE.
Definition: block.cpp:432
void push_back(const Block &element)
Append a sub-element.
Definition: block.cpp:456
void parse() const
Parse TLV-VALUE into sub-elements.
Definition: block.cpp:324
uint32_t type() const
Return the TLV-TYPE of the Block.
Definition: block.hpp:274
unique_ptr< Transform > hexDecode()
Definition: hex-decode.cpp:114
bool isValid() const noexcept
Check if the Block is valid.
Definition: block.hpp:188
const uint8_t * value() const noexcept
Return a raw pointer to the beginning of TLV-VALUE.
Definition: block.cpp:301
bool hasValue() const noexcept
Check if the Block has a non-empty TLV-VALUE.
Definition: block.hpp:287
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:352
const uint8_t * wire() const
Return a raw pointer to the beginning of the encoded wire.
Definition: block.cpp:280
Buffer::const_iterator m_end
Definition: block.hpp:454
bool operator==(const Data &lhs, const Data &rhs)
Definition: data.cpp:312
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:407
uint32_t m_type
TLV-TYPE.
Definition: block.hpp:459
element_const_iterator elements_begin() const
Equivalent to elements().begin()
Definition: block.hpp:399
Block()
Create an invalid Block.
EncodingImpl< EncoderTag > EncodingBuffer
Buffer::const_iterator begin() const
Get begin iterator of encoded wire.
Definition: block.cpp:262
EncodingImpl< EstimatorTag > EncodingEstimator
bool readVarNumber(Iterator &begin, Iterator end, uint64_t &number) noexcept
Read VAR-NUMBER in NDN-TLV encoding.
Definition: tlv.hpp:390
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