Navigation Menu

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 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
wireEncode(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
// ### usage example
namespace ndn {
namespace lp {
static void
example()
{
CachePolicy cp;
cp.setPolicy(CachePolicyType::NO_CACHE);
switch (cp.getPolicy()) {
case CachePolicyType::NO_CACHE:
..
break;
default:
throw std::out_of_range("unexpected CachePolicyType");
break;
}
}
// note: For now, CachePolicy will be converted to equivalent LocalControlHeader.CachingPolicy in NDNLP processing code,
// and LocalControlHeader.CachingPolicy will be used in producer and ContentStore.
// Therefore the example won't be seen in real code, until a Tag for CachePolicy is defined
// that allows direct attachment onto a Data packet.
// See "attachment to a network layer packet" section for more information.
} // 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