Last active
August 29, 2015 14:22
-
-
Save yoursunny/42f44cc308ca0ce9b3ff to your computer and use it in GitHub Desktop.
NDNLPv2 packet format API http://redmine.named-data.net/issues/2841
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// # NDNLPv2 packet format API | |
// ## TLV-TYPE assignments | |
namespace ndn { | |
namespace lp { | |
namespace tlv { | |
enum { | |
LpPacket = 100, | |
// (other TLV-TYPEs omitted) | |
}; | |
} // namespace tlv | |
} // namespace lp | |
} // namespace ndn | |
// ## link layer: packet format | |
namespace ndn { | |
namespace lp { | |
// ---- value types ---- | |
typedef uint64_t Sequence; | |
class NackHeader; // declared in network layer API | |
class CachePolicy; // declared in network layer API | |
// ---- field metadata ---- | |
/** \brief indicates where a field may occur | |
*/ | |
namespace field_location_tags { | |
class Base | |
{ | |
}; | |
/** \brief a header field | |
*/ | |
class Header : public Base | |
{ | |
}; | |
/** \brief the Fragment field | |
*/ | |
class Fragment : public Base | |
{ | |
}; | |
} // namespace field_location_tags | |
/** \brief declares a field | |
*/ | |
CONCEPT struct Field | |
{ | |
/** \brief indicates where this field may occur | |
*/ | |
typedef /* subclass of field_location_tags::Base */ FieldLocation; | |
/** \brief the value type contained in this field | |
*/ | |
typedef /* a DefaultConstructible and CopyAssignable type */ ValueType; | |
/** \brief TLV-TYPE of this field | |
*/ | |
typedef std::integral_constant<uint64_t, /* TLV-TYPE */> TlvType; | |
/** \brief whether this field can occur more than once | |
*/ | |
typedef /* std::true_type or std::false_type */ IsRepeatable; | |
/** \brief decode the field | |
* \param wire a block whose type is TlvType | |
*/ | |
static ValueType | |
decode(const Block& wire); | |
/** \brief encode the field | |
* \tparam Encoder either EncodingBuffer or EncodingEstimator | |
* \param encoder an encoder to prepend the field | |
* \param value the field value | |
*/ | |
template<typename Encoder> | |
static size_t | |
encode(Encoder& encoder, const ValueType& value); | |
}; | |
// note: this is a concept; in real code it should appear as a Boost concept check | |
// ---- field declarations ---- | |
// note: templates and specializations for field declarations go into detail namespace, | |
// public API has typedefs only | |
// note: a field declaration must fulfill Field concept | |
typedef /* ValueType=std::pair<Buffer::const_iterator,Buffer::const_iterator> */ FragmentField; | |
// note on FragmentField: | |
// std::pair<Buffer::const_iterator,Buffer::const_iterator> specifies the fragment boundary. | |
// When writing this field, the iterators are saved but octets are not copied, so caller must maintain the buffer. | |
// When encoding the Packet, the octets are copied. | |
// When reading this field, the iterators refer to the underlying buffer of Packet. | |
// If Packet is changed after reading this field, the iterators will be invalidated. | |
typedef /* ValueType=Sequence */ SequenceField; | |
typedef /* ValueType=uint64_t */ FragIndexField; | |
typedef /* ValueType=uint64_t */ FragCountField; | |
typedef /* ValueType=NackHeader */ NackField; | |
typedef /* ValueType=uint64_t */ NextHopFaceIdField; | |
typedef /* ValueType=CachePolicy */ CachePolicyField; | |
typedef /* ValueType=uint64_t */ IncomingFaceIdField; | |
/** \brief set of all field declarations | |
*/ | |
typedef boost::mpl::set< | |
FragmentField, | |
SequenceField, | |
// (omitted other fields) | |
> FieldSet; | |
/** \brief represents an LpPacket | |
*/ | |
class Packet | |
{ | |
public: | |
Packet(); | |
explicit | |
Packet(const Block& wire); | |
template<encoding::Tag TAG> | |
size_t | |
wireEncode(EncodingImpl<TAG>& encoder) const | |
// note: implementation should use FieldSet, not directly processing fields | |
const Block& | |
wireEncode() const; | |
void | |
wireDecode(const Block& wire); | |
// note: implementation should use FieldSet, not directly processing fields | |
// note: implementation only needs to perform a swallow parse | |
public: // field access | |
/** \return true if FIELD occurs one or more times | |
* \detail This is equivalent to count()>0 | |
*/ | |
template<typename FIELD> | |
bool | |
has() const; | |
/** \return number of occurrences of FIELD | |
*/ | |
template<typename FIELD> | |
size_t | |
count() const; | |
/** \return value of index-th occurrence of FIELD | |
* \throw std::out_of_range if index>=count() | |
*/ | |
template<typename FIELD> | |
FIELD::ValueType | |
get(size_t index = 0); | |
/** \return values of all occurrences of FIELD | |
*/ | |
template<typename FIELD> | |
std::vector<FIELD::ValueType> | |
list(); | |
/** \brief remove all occurrences of FIELD, and add a FIELD with value | |
* \detail This equivalent to clear() followed by add(value) | |
*/ | |
template<typename FIELD> | |
Packet& | |
set(const FIELD::ValueType& value); | |
/** \brief add a FIELD with value | |
* \throw std::length_error if field already exists and is not repeatable | |
*/ | |
template<typename FIELD> | |
Packet& | |
add(const FIELD::ValueType& value); | |
/** \brief remove the index-th occurrence of FIELD | |
* \throw std::out_of_range if index>=count() | |
*/ | |
template<typename FIELD> | |
Packet& | |
remove(size_t index = 0); | |
/** \brief remove all occurrences of FIELD | |
*/ | |
template<typename FIELD> | |
Packet& | |
clear(); | |
private: | |
Block m_wire; | |
// This is the underlying storage. | |
// Field access methods can operate on m_wire.elements() container, | |
// and invoke FIELD::decode and FIELD::encode as necessary. | |
}; | |
} // namespace lp | |
} // namespace ndn | |
// ### usage example | |
namespace ndn { | |
namespace lp { | |
static void | |
example() | |
{ | |
Data data = ..; | |
Block payload = data.wireEncode(); // 5000 octets | |
Packet pkt1; | |
pkt1.set<SequenceField>(2001) | |
.set<FragmentField>(std::make_pair(payload.begin(), payload.begin() + 3000)) | |
.set<FragIndexField>(0) | |
.set<FragCountField>(2) | |
.set<CachePolicyField>(CachePolicy().setPolicy(CachePolicyType::NO_CACHE)); | |
Packet pkt2; | |
pkt2.set<SequenceField>(2002) | |
.set<FragmentField>(std::make_pair(payload.begin() + 3000, payload.end())) | |
.set<FragIndexField>(1) | |
.set<FragCountField(2); | |
pkt2.has<FragIndexField>(); // true | |
pkt2.count<FragIndexField>(); // 1 | |
pkt2.add<FragIndexField>(0); // error: not repeatable | |
pkt2.remove<FragIndexField>(); // OK | |
pkt2.count<FragIndexField>(); // 0 | |
pkt2.remove<FragIndexField>(); // no error | |
pkt2.wireEncode(); // no error: FragIndex is missing but packet format API doesn't care | |
pkt2.count<FragCountField>(); // 1 | |
pkt2.clear<FragCountField>(); // OK | |
pkt2.count<FragCountField>(); // 0 | |
pkt2.has<FragCountField>(); // false | |
Block encoded1 = pkt1.wireEncode(); | |
Packet decoded1(encoded1); | |
decoded1.get<SequenceField>(); // 2001 | |
decoded1.list<SequenceField>(); // std::vector<Sequence>({2001}) | |
decoded1.get<FragmentField>(); // same octets | |
} | |
} // namespace lp | |
} // namespace ndn | |
// ## NACK struct | |
namespace ndn { | |
namespace lp { | |
/** \brief indicates the reason type of a network NACK | |
*/ | |
enum class NackReason { | |
NONE = 0, | |
DUPLICATE = 1, | |
GIVE_UP = 2 | |
}; | |
/** \brief represents a Network NACK header | |
*/ | |
class NackHeader | |
{ | |
public: | |
NackHeader(); | |
explicit | |
NackHeader(const Block& block); | |
template<encoding::Tag TAG> | |
size_t | |
wireEncode(EncodingImpl<TAG>& encoder) const; | |
const Block& | |
wireEncode() const; | |
void | |
wireDecode(const Block& wire); | |
public: // reason | |
/** \return reason code | |
* \retval NackReason::NONE if NackReason element does not exist or has an unknown code | |
*/ | |
NackReason | |
getReason() const; | |
/** \brief set reason code | |
* \param reason a reason code; NackReason::NONE clears the reason | |
*/ | |
NackHeader& | |
setReason(NackReason reason); | |
private: | |
NackReason m_reason; | |
mutable Block m_wire; | |
}; | |
/** \brief represents a Network NACK | |
* \detail This type binds a NackHeader and an Interest, and is intended for use in network layer. | |
*/ | |
class Nack : public TagHost | |
{ | |
public: | |
Nack(); | |
explicit | |
Nack(const Interest& interest); | |
explicit | |
Nack(Interest&& interest); | |
public: // getter/setter | |
const Interest& | |
getInterest() const; | |
Interest& | |
getInterest(); | |
const NackHeader& | |
getHeader() const; | |
NackHeader& | |
getHeader(); | |
Nack& | |
setHeader(const NackHeader& header); | |
Nack& | |
setHeader(NackHeader&& header); | |
public: // NackHeader proxy | |
NackReason | |
getReason() const; | |
Nack& | |
setReason(NackReason reason); | |
private: | |
Interest m_interest; | |
NackHeader m_header; | |
}; | |
} // namespace lp | |
} // namespace ndn | |
// ### usage example | |
namespace ndn { | |
namespace lp { | |
static void | |
exampleUpstreamForwarding() | |
{ | |
Interest interest = ..; | |
Nack nack(std::move(interest)); | |
nack.setReason(NackReason::DUPLICATE); | |
} | |
static void | |
exampleDownstreamForwarding() | |
{ | |
Nack nack = ..; | |
auto pitEntry = pit.lookup(nack.getInterest()); | |
switch (nack.getReason()) { | |
case NackReason::DUPLICATE: | |
.. | |
break; | |
case NackReason::GIVE_UP: | |
.. | |
break; | |
default: | |
// no reason or unknown reason | |
break; | |
} | |
} | |
} // namespace lp | |
} // namespace ndn | |
// ## CachePolicy struct | |
namespace ndn { | |
namespace lp { | |
/** \brief indicates the cache policy applied to a Data packet | |
*/ | |
enum class CachePolicyType { | |
NONE = 0, | |
NO_CACHE = 1 | |
}; | |
/** \brief represents a CachePolicy header | |
*/ | |
class CachePolicy | |
{ | |
public: | |
class Error : public tlv::Error; | |
CachePolicy(); | |
explicit | |
CachePolicy(const Block& block); | |
/** \pre getPolicy() != CachePolicyType::NONE | |
* \throw Error policy type is unset | |
*/ | |
template<encoding::Tag TAG> | |
size_t | |
CachePolicy(EncodingImpl<TAG>& encoder) const; | |
const Block& | |
wireEncode() const; | |
void | |
wireDecode(const Block& wire); | |
public: // policy type | |
/** \return policy type code | |
* \retval CachePolicyType::NONE if policy type is unset or has an unknown code | |
*/ | |
CachePolicyType | |
getPolicy() const; | |
/** \brief set policy type code | |
* \param policy a policy type code; CachePolicyType::NONE clears the policy | |
*/ | |
CachePolicy& | |
setPolicy(CachePolicyType policy); | |
private: | |
CachePolicyType m_policy; | |
mutable Block m_wire; | |
}; | |
} // namespace lp | |
} // namespace ndn | |
// ## attachment to a network layer packet (Interest/Data/Nack) | |
// In general, to attach fields to a network layer packet: | |
// | |
// 1. Declare a subclass of ndn::Tag for an NDNLPv2 feature, | |
// and put all fields related to this feature in the Tag. | |
// 2. Attach this Tag to the network layer packet. | |
// | |
// Not every NDNLPv2 field needs to be exposed to network layer. | |
// When exposing is needed, a Tag is declared per feature, not per field. | |
// Sequence: not exposed to network layer for now; may change in the future. | |
// Fragmentation: completely in link layer, not exposed to network layer. | |
// Nack: passed to network layer as a separate Nack type; it's fundamentally different from an Interest. | |
// NackHopFaceId, CachePolicy, and IncomingFaceId: temporarily reuse LocalControlHeader struct. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment