ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
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
34
35#include <boost/asio/buffer.hpp>
36#include <boost/range/adaptor/reversed.hpp>
37
38#include <algorithm>
39#include <cstring>
40
41namespace ndn {
42
44
45// ---- constructor, creation, assignment ----
46
47Block::Block() = default;
48
49Block::Block(const Block&) = default;
50
51Block&
52Block::operator=(const Block&) = default;
53
54Block::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
78Block::Block(const EncodingBuffer& buffer)
79 : Block(buffer.getBuffer(), buffer.begin(), buffer.end(), true)
80{
81}
82
84 : Block(buffer, buffer->begin(), buffer->end(), true)
85{
86}
87
88Block::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
123Block::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
136Block::Block(uint32_t type)
137 : m_type(type)
138 , m_size(tlv::sizeOfVarNumber(m_type) + tlv::sizeOfVarNumber(0))
139{
140}
141
142Block::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
153Block::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
164std::tuple<bool, Block>
165Block::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
192std::tuple<bool, Block>
193Block::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
221Block
222Block::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
255const uint8_t*
257{
258 if (!hasWire())
259 NDN_THROW(Error("Underlying wire buffer is empty"));
260
261 return &*m_begin;
262}
263
264size_t
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
285{
286 if (!hasWire())
287 NDN_THROW(Error("Underlying wire buffer is empty"));
288
289 return m_end;
290}
291
292void
293Block::reset() noexcept
294{
295 *this = {};
296}
297
298void
300{
301 m_buffer.reset(); // discard underlying buffer by resetting shared_ptr
303}
304
305// ---- value ----
306
307const uint8_t*
308Block::value() const noexcept
309{
310 return value_size() > 0 ? &*m_valueBegin : nullptr;
311}
312
313Block
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
325void
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
352void
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
365size_t
366Block::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
378size_t
379Block::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
388size_t
389Block::encode(EncodingBuffer& encoder)
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 }
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
413const Block&
414Block::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
425Block::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
431void
432Block::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
455void
456Block::push_back(const Block& element)
457{
458 resetWire();
459 m_elements.push_back(element);
460}
461
462void
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
478Block::operator boost::asio::const_buffer() const
479{
480 return {data(), size()};
481}
482
483bool
484Block::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
492void
493Block::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
519inline namespace literals {
520inline namespace block_literals {
521
522Block
523operator ""_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
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
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
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
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:420
constexpr bool readVarNumber(Iterator &begin, Iterator end, uint64_t &number) noexcept
Read VAR-NUMBER in NDN-TLV encoding.
Definition tlv.hpp:364
constexpr bool readType(Iterator &begin, Iterator end, uint32_t &type) noexcept
Read TLV-TYPE.
Definition tlv.hpp:382
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
STL namespace.
Backport of ostream_joiner from the Library Fundamentals v2 TS.