Skip to content

Instantly share code, notes, and snippets.

@yoursunny
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yoursunny/42f44cc308ca0ce9b3ff to your computer and use it in GitHub Desktop.
Save yoursunny/42f44cc308ca0ce9b3ff to your computer and use it in GitHub Desktop.
NDNLPv2 packet format API http://redmine.named-data.net/issues/2841
// # 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