NS-3 based Named Data Networking (NDN) simulator
ndnSIM: NDN, CCN, CCNx, content centric networks
API Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
wire-ccnb-data.cc
1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
2 /*
3  * Copyright (c) 2011 University of California, Los Angeles
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Ilya Moiseenko <iliamo@cs.ucla.edu>
19  * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
20  */
21 
22 #include "../ccnb.h"
23 
24 #include "wire-ccnb.h"
25 
26 #include "ns3/log.h"
27 
28 #include "ccnb-parser/common.h"
29 #include "ccnb-parser/visitors/void-depth-first-visitor.h"
30 #include "ccnb-parser/visitors/name-visitor.h"
31 #include "ccnb-parser/visitors/non-negative-integer-visitor.h"
32 #include "ccnb-parser/visitors/timestamp-visitor.h"
33 #include "ccnb-parser/visitors/string-visitor.h"
34 #include "ccnb-parser/visitors/uint32t-blob-visitor.h"
35 #include "ccnb-parser/visitors/content-type-visitor.h"
36 
37 #include "ccnb-parser/syntax-tree/block.h"
38 #include "ccnb-parser/syntax-tree/dtag.h"
39 
40 #include <boost/foreach.hpp>
41 
42 NS_LOG_COMPONENT_DEFINE ("ndn.wire.Ccnb.Data");
43 
44 NDN_NAMESPACE_BEGIN
45 
46 namespace wire {
47 namespace ccnb {
48 
49 // const std::string DefaultDigestAlgorithm = "2.16.840.1.101.3.4.2.1";
50 
51 class DataTrailer : public Trailer
52 {
53 public:
54  DataTrailer ()
55  {
56  }
57 
58  static TypeId GetTypeId ()
59  {
60  static TypeId tid = TypeId ("ns3::ndn::Data::Ccnb::Closer")
61  .SetGroupName ("Ndn")
62  .SetParent<Trailer> ()
63  .AddConstructor<DataTrailer> ()
64  ;
65  return tid;
66  }
67 
68  virtual TypeId GetInstanceTypeId (void) const
69  {
70  return GetTypeId ();
71  }
72 
73  virtual void Print (std::ostream &os) const
74  {
75  }
76 
77  virtual uint32_t GetSerializedSize (void) const
78  {
79  return 2;
80  }
81 
82  virtual void Serialize (Buffer::Iterator end) const
83  {
84  Buffer::Iterator i = end;
85  i.Prev (2); // Trailer interface requires us to go backwards
86 
87  i.WriteU8 (0x00); // </Content>
88  i.WriteU8 (0x00); // </Data>
89  }
90 
91  virtual uint32_t Deserialize (Buffer::Iterator end)
92  {
93  Buffer::Iterator i = end;
94  i.Prev (2); // Trailer interface requires us to go backwards
95 
96  uint8_t closing_tag_content = i.ReadU8 ();
97  NS_ASSERT_MSG (closing_tag_content==0, "Should be a closing tag </Content> (0x00)");
98 
99  uint8_t closing_tag_content_object = i.ReadU8 ();
100  NS_ASSERT_MSG (closing_tag_content_object==0, "Should be a closing tag </Data> (0x00)");
101 
102  return 2;
103  }
104 };
105 
106 NS_OBJECT_ENSURE_REGISTERED (Data);
107 NS_OBJECT_ENSURE_REGISTERED (DataTrailer);
108 
109 TypeId
110 Data::GetTypeId (void)
111 {
112  static TypeId tid = TypeId ("ns3::ndn::Data::Ccnb")
113  .SetGroupName ("Ndn")
114  .SetParent<Header> ()
115  .AddConstructor<Data> ()
116  ;
117  return tid;
118 }
119 
120 TypeId
121 Data::GetInstanceTypeId (void) const
122 {
123  return GetTypeId ();
124 }
125 
126 Data::Data ()
127  : m_data (Create<ndn::Data> ())
128 {
129 }
130 
131 Data::Data (Ptr<ndn::Data> data)
132  : m_data (data)
133 {
134 }
135 
136 Ptr<ndn::Data>
137 Data::GetData ()
138 {
139  return m_data;
140 }
141 
142 Ptr<Packet>
143 Data::ToWire (Ptr<const ndn::Data> data)
144 {
145  static DataTrailer trailer;
146 
147  Ptr<const Packet> p = data->GetWire ();
148  if (!p)
149  {
150  Ptr<Packet> packet = Create<Packet> (*data->GetPayload ());
151  Data wireEncoding (ConstCast<ndn::Data> (data));
152  packet->AddHeader (wireEncoding);
153  packet->AddTrailer (trailer);
154  data->SetWire (packet);
155 
156  p = packet;
157  }
158 
159  return p->Copy ();
160 }
161 
162 Ptr<ndn::Data>
163 Data::FromWire (Ptr<Packet> packet)
164 {
165  static DataTrailer trailer;
166 
167  Ptr<ndn::Data> data = Create<ndn::Data> ();
168  Ptr<Packet> wire = packet->Copy ();
169 
170  Data wireEncoding (data);
171  packet->RemoveHeader (wireEncoding);
172  packet->RemoveTrailer (trailer);
173 
174  data->SetPayload (packet);
175  data->SetWire (wire);
176 
177  return data;
178 }
179 
180 void
181 Data::Serialize (Buffer::Iterator start) const
182 {
183  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_Data, CcnbParser::CCN_DTAG); // <Data>
184 
185  // fake signature
186  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_Signature, CcnbParser::CCN_DTAG); // <Signature>
187  // Signature ::= √DigestAlgorithm?
188  // Witness?
189  // √SignatureBits
190  // if (GetSignature ().GetDigestAlgorithm () != Signature::DefaultDigestAlgorithm)
191  // {
192  // Ccnb::AppendString (start, CcnbParser::CCN_DTAG_DigestAlgorithm, GetSignature ().GetDigestAlgorithm ());
193  // }
194  Ccnb::AppendString (start, CcnbParser::CCN_DTAG_DigestAlgorithm, "NOP");
195  Ccnb::AppendTaggedBlobWithPadding (start, CcnbParser::CCN_DTAG_SignatureBits, 16, m_data->GetSignature ()); // <SignatureBits />
196  Ccnb::AppendCloser (start); // </Signature>
197 
198  // Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_Name, CcnbParser::CCN_DTAG); // <Name>
199  Ccnb::SerializeName (start, m_data->GetName()); // <Component>...</Component>...
200  // Ccnb::AppendCloser (start); // </Name>
201 
202  // fake signature
203  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_SignedInfo, CcnbParser::CCN_DTAG); // <SignedInfo>
204  // SignedInfo ::= √PublisherPublicKeyDigest
205  // √Timestamp
206  // √Type?
207  // √FreshnessSeconds?
208  // FinalBlockID?
209  // KeyLocator?
210  // Ccnb::AppendTaggedBlob (start, CcnbParser::CCN_DTAG_PublisherPublicKeyDigest, // <PublisherPublicKeyDigest>...
211  // GetSignedInfo ().GetPublisherPublicKeyDigest ());
212 
213  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_Timestamp, CcnbParser::CCN_DTAG); // <Timestamp>...
214  Ccnb::AppendTimestampBlob (start, m_data->GetTimestamp ());
215  Ccnb::AppendCloser (start);
216 
217  // if (GetSignedInfo ().GetContentType () != DATA)
218  // {
219  // uint8_t type[3];
220  // type[0] = (GetSignedInfo ().GetContentType () >> 16) & 0xFF;
221  // type[1] = (GetSignedInfo ().GetContentType () >> 8 ) & 0xFF;
222  // type[2] = (GetSignedInfo ().GetContentType () ) & 0xFF;
223 
224  // Ccnb::AppendTaggedBlob (start, CCN_DTAG_Type, type, 3);
225  // }
226  if (m_data->GetFreshness () > Seconds(0))
227  {
228  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_FreshnessSeconds, CcnbParser::CCN_DTAG);
229  Ccnb::AppendNumber (start, m_data->GetFreshness ().ToInteger (Time::S));
230  Ccnb::AppendCloser (start);
231  }
232  if (m_data->GetKeyLocator () != 0)
233  {
234  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_KeyLocator, CcnbParser::CCN_DTAG); // <KeyLocator>
235  {
236  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_KeyName, CcnbParser::CCN_DTAG); // <KeyName>
237  {
238  // Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_Name, CcnbParser::CCN_DTAG); // <Name>
239  Ccnb::SerializeName (start, *m_data->GetKeyLocator ()); // <Component>...</Component>...
240  // Ccnb::AppendCloser (start); // </Name>
241  }
242  Ccnb::AppendCloser (start); // </KeyName>
243  }
244  Ccnb::AppendCloser (start); // </KeyLocator>
245  }
246 
247  Ccnb::AppendCloser (start); // </SignedInfo>
248 
249  Ccnb::AppendBlockHeader (start, CcnbParser::CCN_DTAG_Content, CcnbParser::CCN_DTAG); // <Content>
250 
251  uint32_t payloadSize = m_data->GetPayload ()->GetSize ();
252  if (payloadSize > 0)
253  Ccnb::AppendBlockHeader (start, payloadSize, CcnbParser::CCN_BLOB);
254 
255  // there are no closing tags !!!
256  // The closing tag is handled by DataTail
257 }
258 
259 uint32_t
260 Data::GetSerializedSize () const
261 {
262  size_t written = 0;
263  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_Data); // <Data>
264 
265  // fake signature
266  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_Signature); // <Signature>
267  // Signature ::= DigestAlgorithm?
268  // Witness?
269  // SignatureBits
270  // if (GetSignature ().GetDigestAlgorithm () != Signature::DefaultDigestAlgorithm)
271  // {
272  // written += Ccnb::EstimateString (CcnbParser::CCN_DTAG_DigestAlgorithm, GetSignature ().GetDigestAlgorithm ());
273  // }
274  written += Ccnb::EstimateString (CcnbParser::CCN_DTAG_DigestAlgorithm, "NOP");
275  // "signature" will be always padded to 16 octets
276  written += Ccnb::EstimateTaggedBlob (CcnbParser::CCN_DTAG_SignatureBits, 16); // <SignatureBits />
277  // written += Ccnb::EstimateTaggedBlob (CcnbParser::CCN_DTAG_SignatureBits, sizeof (m_data->GetSignature ())); // <SignatureBits />
278  written += 1; // </Signature>
279 
280  //written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_Name); // <Name>
281  written += Ccnb::SerializedSizeName (m_data->GetName ()); // <Component>...</Component>...
282  //written += 1; // </Name>
283 
284  // fake signature
285  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_SignedInfo); // <SignedInfo>
286  // SignedInfo ::= √PublisherPublicKeyDigest
287  // √Timestamp
288  // √Type?
289  // √FreshnessSeconds?
290  // FinalBlockID?
291  // KeyLocator?
292 
293  // written += Ccnb::EstimateTaggedBlob (CCN_DTAG_PublisherPublicKeyDigest, // <PublisherPublicKeyDigest>...
294  // sizeof (GetSignedInfo ().GetPublisherPublicKeyDigest ()));
295 
296  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_Timestamp); // <Timestamp>...
297  written += Ccnb::EstimateTimestampBlob (m_data->GetTimestamp ());
298  written += 1;
299 
300  // if (GetSignedInfo ().GetContentType () != DATA)
301  // {
302  // written += Ccnb::EstimateTaggedBlob (CcnbParser::CCN_DTAG_Type, 3);
303  // }
304  if (m_data->GetFreshness () > Seconds(0))
305  {
306  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_FreshnessSeconds);
307  written += Ccnb::EstimateNumber (m_data->GetFreshness ().ToInteger (Time::S));
308  written += 1;
309  }
310 
311  if (m_data->GetKeyLocator () != 0)
312  {
313  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_KeyLocator); // <KeyLocator>
314  {
315  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_KeyName); // <KeyName>
316  {
317  //written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_Name); // <Name>
318  written += Ccnb::SerializedSizeName (*m_data->GetKeyLocator ()); // <Component>...</Component>...
319  //written += 1; // </Name>
320  }
321  written += 1; // </KeyName>
322  }
323  written += 1; // </KeyLocator>
324  }
325 
326  written += 1; // </SignedInfo>
327 
328  written += Ccnb::EstimateBlockHeader (CcnbParser::CCN_DTAG_Content); // <Content>
329 
330  uint32_t payloadSize = m_data->GetPayload ()->GetSize ();
331  if (payloadSize > 0)
332  written += Ccnb::EstimateBlockHeader (payloadSize);
333 
334  // there are no closing tags !!!
335  // The closing tag is handled by DataTail
336  return written;
337 }
338 
340 {
341 public:
342  virtual void visit (CcnbParser::Dtag &n, boost::any param/*should be Data* */)
343  {
344  // uint32_t n.m_dtag;
345  // std::list< Ptr<CcnbParser::Block> > n.m_nestedBlocks;
346  static CcnbParser::NameVisitor nameVisitor;
347  static CcnbParser::NonNegativeIntegerVisitor nonNegativeIntegerVisitor;
348  static CcnbParser::TimestampVisitor timestampVisitor;
349  static CcnbParser::StringVisitor stringVisitor;
350  static CcnbParser::Uint32tBlobVisitor uint32tBlobVisitor;
351  static CcnbParser::ContentTypeVisitor contentTypeVisitor;
352 
353  ndn::Data &contentObject = *(boost::any_cast<ndn::Data*> (param));
354 
355  switch (n.m_dtag)
356  {
357  case CcnbParser::CCN_DTAG_Data:
358  // process nested blocks
359  BOOST_FOREACH (Ptr<CcnbParser::Block> block, n.m_nestedTags)
360  {
361  block->accept (*this, param);
362  }
363  break;
364  case CcnbParser::CCN_DTAG_Name:
365  {
366  // process name components
367  Ptr<Name> name = Create<Name> ();
368  n.accept (nameVisitor, GetPointer (name));
369  contentObject.SetName (name);
370  break;
371  }
372 
373  case CcnbParser::CCN_DTAG_Signature:
374  // process nested blocks
375  BOOST_FOREACH (Ptr<CcnbParser::Block> block, n.m_nestedTags)
376  {
377  block->accept (*this, param);
378  }
379  break;
380 
381  // case CCN_DTAG_DigestAlgorithm:
382  // NS_LOG_DEBUG ("DigestAlgorithm");
383  // if (n.m_nestedTags.size ()!=1) // should be exactly one UDATA inside this tag
384  // throw CcnbParser::CcnbDecodingException ();
385 
386  // contentObject.GetSignature ().SetDigestAlgorithm
387  // (boost::any_cast<std::string> ((*n.m_nestedTags.begin())->accept
388  // (stringVisitor)));
389  // break;
390 
391  case CcnbParser::CCN_DTAG_SignatureBits:
392  NS_LOG_DEBUG ("SignatureBits");
393  if (n.m_nestedTags.size ()!=1) // should be only one nested tag
395 
396  contentObject.SetSignature
397  (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
398  (uint32tBlobVisitor)));
399  break;
400 
401  case CcnbParser::CCN_DTAG_SignedInfo:
402  // process nested blocks
403  BOOST_FOREACH (Ptr<CcnbParser::Block> block, n.m_nestedTags)
404  {
405  block->accept (*this, param);
406  }
407  break;
408 
409  // case CCN_DTAG_PublisherPublicKeyDigest:
410  // NS_LOG_DEBUG ("PublisherPublicKeyDigest");
411  // if (n.m_nestedTags.size ()!=1) // should be only one nested tag
412  // throw CcnbParser::CcnbDecodingException ();
413 
414  // contentObject.GetSignedInfo ().SetPublisherPublicKeyDigest
415  // (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
416  // (uint32tBlobVisitor)));
417  // break;
418 
419  case CcnbParser::CCN_DTAG_Timestamp:
420  NS_LOG_DEBUG ("Timestamp");
421  if (n.m_nestedTags.size()!=1) // should be exactly one nested tag
423 
424  contentObject.SetTimestamp
425  (boost::any_cast<Time> ((*n.m_nestedTags.begin())->accept
426  (timestampVisitor)));
427  break;
428 
429  // case CCN_DTAG_Type:
430  // NS_LOG_DEBUG ("Type");
431  // if (n.m_nestedTags.size ()!=1) // should be only one nested tag
432  // throw CcnbParser::CcnbDecodingException ();
433 
434  // contentObject.GetSignedInfo ().SetContentType
435  // (static_cast<Data::ContentType>
436  // (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
437  // (contentTypeVisitor))));
438  // break;
439 
440  case CcnbParser::CCN_DTAG_FreshnessSeconds:
441  NS_LOG_DEBUG ("FreshnessSeconds");
442 
443  if (n.m_nestedTags.size()!=1) // should be exactly one nested tag
445 
446  contentObject.SetFreshness
447  (Seconds
448  (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
449  (nonNegativeIntegerVisitor))));
450  break;
451 
452  case CcnbParser::CCN_DTAG_KeyLocator:
453  // process nested blocks
454  BOOST_FOREACH (Ptr<CcnbParser::Block> block, n.m_nestedTags)
455  {
456  block->accept (*this, param);
457  }
458  break;
459 
460  case CcnbParser::CCN_DTAG_KeyName:
461  {
462  if (n.m_nestedTags.size ()!=1) // should be exactly one nested tag
464 
465  // process name components
466  Ptr<Name> name = Create<Name> ();
467  n.accept (nameVisitor, GetPointer (name));
468  contentObject.SetKeyLocator (name);
469  break;
470  }
471 
472  case CcnbParser::CCN_DTAG_Content: // !!! HACK
473  // This hack was necessary for memory optimizations (i.e., content is virtual payload)
474  NS_ASSERT_MSG (n.m_nestedTags.size() == 0, "Parser should have stopped just after processing <Content> tag");
475  break;
476 
477  default: // ignore all other stuff
478  break;
479  }
480  }
481 };
482 
483 uint32_t
484 Data::Deserialize (Buffer::Iterator start)
485 {
486  static DataVisitor contentObjectVisitor;
487 
488  Buffer::Iterator i = start;
489  Ptr<CcnbParser::Block> root = CcnbParser::Block::ParseBlock (i);
490  root->accept (contentObjectVisitor, GetPointer (m_data));
491 
492  return i.GetDistanceFrom (start);
493 }
494 
495 void
496 Data::Print (std::ostream &os) const
497 {
498  os << "D: " << m_data->GetName ();
499  // os << "<Data><Name>" << GetName () << "</Name><Content>";
500 }
501 
502 } // ccnb
503 } // wire
504 
505 NDN_NAMESPACE_END
Depth-first visitor that takes one argument and returns nothing.
Visitor to obtain nonce value from BLOB block.
static size_t EstimateBlockHeader(size_t value)
Estimate size of the CCNB block header.
Definition: wire-ccnb.cc:62
static size_t EstimateNumber(uint32_t number)
Estimate size of the number in CCNB encoding.
Definition: wire-ccnb.cc:89
Routines to serialize/deserialize NDN Data packet in ccnb format.
Definition: ccnb.h:66
std::list< Ptr< Block > > m_nestedTags
List of nested tags.
Definition: base-tag.h:43
static size_t AppendTaggedBlobWithPadding(Buffer::Iterator &start, uint32_t dtag, uint32_t length, const uint8_t *data, size_t size)
Append a tagged BLOB, adding 0-byte padding if necessary.
Definition: wire-ccnb.cc:161
static size_t SerializeName(Buffer::Iterator &start, const Name &name)
Append Name in CCNB encoding.
Definition: wire-ccnb.cc:220
Exception thrown if there is a parsing error.
Definition: common.h:50
static size_t EstimateString(uint32_t dtag, const std::string &string)
Estimate size of the string in CCNB encoding.
Definition: wire-ccnb.cc:214
static size_t EstimateTimestampBlob(const Time &time)
Estimate size of a binary timestamp as a BLOB using CCNB enconding.
Definition: wire-ccnb.cc:131
virtual void visit(CcnbParser::Dtag &n, boost::any param)
Method accepting DTAG block.
Class to represent DTAG ccnb-encoded node.
Definition: dtag.h:37
static size_t AppendTimestampBlob(Buffer::Iterator &start, const Time &time)
Append a binary timestamp as a BLOB using the ccn binary Timestamp representation (12-bit fraction)...
Definition: wire-ccnb.cc:104
static size_t AppendBlockHeader(Buffer::Iterator &start, size_t value, uint32_t block_type)
Append CCNB block header.
Definition: wire-ccnb.cc:43
void SetTimestamp(const Time &timestamp)
Set content object timestamp.
Definition: ndn-data.cc:89
static size_t EstimateTaggedBlob(uint32_t dtag, size_t size)
Estimate size of a tagged BLOB in CCNB enconding.
Definition: wire-ccnb.cc:190
Data header.
Definition: ndn-data.h:39
static size_t AppendNumber(Buffer::Iterator &start, uint32_t number)
Add number in CCNB encoding.
Definition: wire-ccnb.cc:75
static size_t AppendString(Buffer::Iterator &start, uint32_t dtag, const std::string &string)
Append a tagged string (should be a valid UTF-8 coded string)
Definition: wire-ccnb.cc:199
void SetKeyLocator(Ptr< Name > keyLocator)
Set key locator.
Definition: ndn-data.cc:129
uint32_t m_dtag
Dictionary code for DTAG.
Definition: dtag.h:60
void SetFreshness(const Time &freshness)
Set freshness of the content object.
Definition: ndn-data.cc:102
Visitor to obtain fill CcnxName object with name components.
Definition: name-visitor.h:35
Visitor to obtain non-negative integer value from UDATA block.
Visitor to obtain string value from UDATA block.
static size_t AppendCloser(Buffer::Iterator &start)
Append CCNB closer tag (estimated size is 1)
Definition: wire-ccnb.cc:97
static Ptr< Block > ParseBlock(Buffer::Iterator &start, bool dontParseBlock=false)
Parsing stream (recursively) and creating a parsed BLOCK object.
Definition: block.cc:49
Visitor to obtain timestamp value from BLOB block.
static size_t SerializedSizeName(const Name &name)
Estimate size of Name in CCNB encoding.
Definition: wire-ccnb.cc:234
Visitor to obtain nonce value from BLOB block.
void SetName(Ptr< Name > name)
Set content object name.
Definition: ndn-data.cc:61
void SetSignature(uint32_t signature)
Set "fake" signature on the content object.
Definition: ndn-data.cc:116
virtual void accept(VoidNoArguVisitor &v)
Accept visitor void(*)()
Definition: dtag.h:55