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 programmable 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 Nack; // declared in network layer API | |
typedef uint64_t FaceId; | |
enum class CachingPolicy; // 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; | |
typedef /* ValueType=Sequence */ SequenceField; | |
typedef /* ValueType=uint64_t */ FragIndexField; | |
typedef /* ValueType=uint64_t */ FragCountField; | |
typedef /* ValueType=Nack */ NackField; | |
typedef /* ValueType=FaceId */ NextHopFaceIdField; | |
typedef /* ValueType=CachingPolicy */ CachingPolicyField; | |
typedef /* ValueType=FaceId */ IncomingFaceIdField; | |
// 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. | |
/** \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<typename Encoder> | |
size_t | |
wireEncode(Encoder& encoder); | |
// 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<CachingPolicyField>(CachingPolicy::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 NackReasonType { | |
NONE = 0, | |
DUPLICATE = tlv::DuplicateNack, | |
GIVE_UP = tlv::GiveUpNack | |
}; | |
/** \brief represents a network NACK reason | |
*/ | |
class NackReason | |
{ | |
public: | |
NackReason(); | |
explicit | |
NackReason(const Block& wire); | |
template<typename Encoder> | |
virtual size_t | |
wireEncode(Encoder& encoder) = 0; | |
const Block& | |
wireEncode() const; | |
virtual void | |
wireDecode(const Block& wire) = 0; | |
NackReasonType | |
getType() const; | |
}; | |
CONCEPT class NackReasonConcrete : public NackReason | |
{ | |
public: | |
/** \brief TLV-TYPE of this field | |
*/ | |
typedef std::integral_constant<uint64_t, /* TLV-TYPE */> TlvType; | |
}; | |
// note: this is a concept; in real code it should appear as a Boost concept check | |
namespace detail { | |
template<uint64_t TLVTYPE> | |
class NackReasonEmptyElement : public NackReason | |
{ | |
public: | |
template<typename Encoder> | |
virtual size_t | |
wireEncode(Encoder& encoder) final; | |
virtual void | |
wireDecode(const Block& wire) final; | |
typedef std::integral_constant<uint64_t, TLVTYPE> TlvType; | |
}; | |
} // namespace detail | |
class Duplicate : public detail::NackReasonEmptyElement<tlv::Duplicate> | |
{ | |
}; | |
class GiveUp : public detail::NackReasonEmptyElement<tlv::GiveUp> | |
{ | |
}; | |
/** \brief set of all NACK reasons | |
*/ | |
typedef boost::mpl::set< | |
Duplicate, | |
GiveUp | |
> NackReasonSet; | |
/** \brief represents a network NACK | |
*/ | |
class Nack : public TagHost | |
{ | |
public: | |
Nack(); | |
explicit | |
Nack(const Interest& interest); | |
explicit | |
Nack(Interest&& interest); | |
public: | |
const Interest& | |
getInterest() const; | |
/** \return reason type | |
* \retval NackReasonType::NONE if reason element does not exist | |
*/ | |
NackReasonType | |
getReasonType() const; | |
// note: implementation should not construct NackReason object in this call | |
/** \brief set reason type | |
* \detail This is equivalent to calling setReason with a default-constructed reason element. | |
* setReasonType(NackReasonType::NONE) removes the reason element. | |
*/ | |
Nack& | |
setReasonType(NackReasonType reasonType); | |
// note: implementation should use NackReasonSet | |
/** \return the reason element | |
*/ | |
NackReason | |
getReason() const; | |
// note: implementation should use NackReasonSet to find the correct concrete reason class | |
/** \return the reason element as specified class | |
* \warning undefined behavior if reason type does not match REASON | |
*/ | |
template<typename REASON> | |
REASON | |
getReason() const; | |
/** \brief replace reason element | |
*/ | |
template<typename REASON> | |
Nack& | |
setReason(const REASON& reason); | |
}; | |
} // namespace lp | |
} // namespace ndn | |
// ## CachingPolicy struct | |
namespace ndn { | |
namespace lp { | |
/** \brief indicates the caching policy applied to a Data packet | |
*/ | |
enum class CachingPolicy { | |
NONE = 0, | |
NO_CACHE = tlv::NoCache | |
}; | |
// Eventually CachingPolicy should take the same approach as NackReason, | |
// but it's anticipated that this field would keep as NONE/NO_CACHE for some time, | |
// so let's start with a simple enum. | |
} // namespace lp | |
} // namespace ndn | |
// ## attachment to Interest/Data | |
// 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, CachingPolicy, and IncomingFaceId: temporarily reuse LocalControlHeader struct. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment