blob: 7007daf1f8cf2ffc70a49ce6ce7891fb2a134d83 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2013-2016 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
* ndn-cxx library is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
*/
#include "exclude.hpp"
#include "encoding/block-helpers.hpp"
#include <boost/range/adaptors.hpp>
namespace ndn {
Exclude::ExcludeComponent::ExcludeComponent(const name::Component& component1)
: isNegInf(false)
, component(component1)
{
}
Exclude::ExcludeComponent::ExcludeComponent(bool isNegInf1)
: isNegInf(true)
{
BOOST_ASSERT(isNegInf1 == true);
}
bool
operator==(const Exclude::ExcludeComponent& a, const Exclude::ExcludeComponent& b)
{
return (a.isNegInf && b.isNegInf) ||
(a.isNegInf == b.isNegInf && a.component == b.component);
}
bool
operator>(const Exclude::ExcludeComponent& a, const Exclude::ExcludeComponent& b)
{
return a.isNegInf < b.isNegInf ||
(a.isNegInf == b.isNegInf && a.component > b.component);
}
bool
Exclude::Range::operator==(const Exclude::Range& other) const
{
return this->fromInfinity == other.fromInfinity && this->toInfinity == other.toInfinity &&
(this->fromInfinity || this->from == other.from) &&
(this->toInfinity || this->to == other.to);
}
std::ostream&
operator<<(std::ostream& os, const Exclude::Range& range)
{
if (range.isSingular()) {
return os << '{' << range.from << '}';
}
if (range.fromInfinity) {
os << "(-∞";
}
else {
os << '[' << range.from;
}
os << ",";
if (range.toInfinity) {
os << "+∞)";
}
else {
os << range.to << ']';
}
return os;
}
BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Exclude>));
BOOST_CONCEPT_ASSERT((WireEncodable<Exclude>));
BOOST_CONCEPT_ASSERT((WireEncodableWithEncodingBuffer<Exclude>));
BOOST_CONCEPT_ASSERT((WireDecodable<Exclude>));
static_assert(std::is_base_of<tlv::Error, Exclude::Error>::value,
"Exclude::Error must inherit from tlv::Error");
Exclude::Exclude() = default;
Exclude::Exclude(const Block& wire)
{
wireDecode(wire);
}
template<encoding::Tag TAG>
size_t
Exclude::wireEncode(EncodingImpl<TAG>& encoder) const
{
if (m_entries.empty()) {
BOOST_THROW_EXCEPTION(Error("cannot encode empty Exclude selector"));
}
size_t totalLength = 0;
// Exclude ::= EXCLUDE-TYPE TLV-LENGTH Any? (NameComponent (Any)?)+
// Any ::= ANY-TYPE TLV-LENGTH(=0)
for (const Entry& entry : m_entries) {
if (entry.second) {
totalLength += prependEmptyBlock(encoder, tlv::Any);
}
if (!entry.first.isNegInf) {
totalLength += entry.first.component.wireEncode(encoder);
}
}
totalLength += encoder.prependVarNumber(totalLength);
totalLength += encoder.prependVarNumber(tlv::Exclude);
return totalLength;
}
template size_t
Exclude::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& encoder) const;
template size_t
Exclude::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& encoder) const;
const Block&
Exclude::wireEncode() const
{
if (m_wire.hasWire())
return m_wire;
EncodingEstimator estimator;
size_t estimatedSize = wireEncode(estimator);
EncodingBuffer buffer(estimatedSize, 0);
wireEncode(buffer);
m_wire = buffer.block();
return m_wire;
}
void
Exclude::wireDecode(const Block& wire)
{
clear();
if (wire.type() != tlv::Exclude)
BOOST_THROW_EXCEPTION(tlv::Error("Unexpected TLV type when decoding Exclude"));
m_wire = wire;
m_wire.parse();
if (m_wire.elements_size() == 0) {
BOOST_THROW_EXCEPTION(Error("Exclude element cannot be empty"));
}
// Exclude ::= EXCLUDE-TYPE TLV-LENGTH Any? (NameComponent (Any)?)+
// Any ::= ANY-TYPE TLV-LENGTH(=0)
Block::element_const_iterator i = m_wire.elements_begin();
if (i->type() == tlv::Any) {
this->appendEntry(true, true);
++i;
}
while (i != m_wire.elements_end()) {
name::Component component;
try {
component = name::Component(*i);
}
catch (const name::Component::Error&) {
BOOST_THROW_EXCEPTION(Error("Incorrect format of Exclude filter"));
}
++i;
if (i != m_wire.elements_end() && i->type() == tlv::Any) {
this->appendEntry(component, true);
++i;
}
else {
this->appendEntry(component, false);
}
}
}
template<typename T>
void
Exclude::appendEntry(const T& component, bool hasAny)
{
m_entries.emplace_hint(m_entries.begin(), std::piecewise_construct,
std::forward_as_tuple(component),
std::forward_as_tuple(hasAny));
}
// example: ANY "b" "d" ANY "f"
// ordered in map as: "f" (false); "d" (true); "b" (false); -Inf (true)
//
// lower_bound("") -> -Inf (true) <-- excluded (ANY)
// lower_bound("a") -> -Inf (true) <-- excluded (ANY)
// lower_bound("b") -> "b" (false) <--- excluded (equal)
// lower_bound("c") -> "b" (false) <--- not excluded (not equal and no ANY)
// lower_bound("d") -> "d" (true) <- excluded
// lower_bound("e") -> "d" (true) <- excluded
bool
Exclude::isExcluded(const name::Component& comp) const
{
ExcludeMap::const_iterator lb = m_entries.lower_bound(comp);
return lb != m_entries.end() && // if false, comp is less than the first excluded component
(lb->second || // comp matches an ANY range
(!lb->first.isNegInf && lb->first.component == comp)); // comp equals an exact excluded component
}
Exclude&
Exclude::excludeOne(const name::Component& comp)
{
if (!isExcluded(comp)) {
this->appendEntry(comp, false);
m_wire.reset();
}
return *this;
}
Exclude&
Exclude::excludeBefore(const name::Component& to)
{
return excludeRange(ExcludeComponent(true), to);
}
Exclude&
Exclude::excludeRange(const name::Component& from, const name::Component& to)
{
return excludeRange(ExcludeComponent(from), to);
}
// example: ANY "b" "d" ANY "g"
// ordered in map as: "f" (false); "d" (true); "b" (false); -Inf (true)
// possible sequence of operations:
// excludeBefore("a") -> excludeRange(-Inf, "a") -> ANY "a"
// "a" (false); -Inf (true)
// excludeBefore("b") -> excludeRange(-Inf, "b") -> ANY "b"
// "b" (false); -Inf (true)
// excludeRange("e", "g") -> ANY "b" "e" ANY "g"
// "g" (false); "e" (true); "b" (false); -Inf (true)
// excludeRange("d", "f") -> ANY "b" "d" ANY "g"
// "g" (false); "d" (true); "b" (false); -Inf (true)
Exclude&
Exclude::excludeRange(const ExcludeComponent& from, const name::Component& to)
{
if (!from.isNegInf && from.component >= to) {
BOOST_THROW_EXCEPTION(Error("Invalid exclude range [" + from.component.toUri() + ", " + to.toUri() + "] "
"(for single name exclude use Exclude::excludeOne)"));
}
ExcludeMap::iterator newFrom = m_entries.lower_bound(from);
if (newFrom == m_entries.end() || !newFrom->second /*without ANY*/) {
bool isNewEntry = false;
std::tie(newFrom, isNewEntry) = m_entries.emplace(from, true);
if (!isNewEntry) {
// this means that the lower bound is equal to the item itself. So, just update ANY flag
newFrom->second = true;
}
}
// else
// nothing special if start of the range already exists with ANY flag set
ExcludeMap::iterator newTo = m_entries.lower_bound(to);
BOOST_ASSERT(newTo != m_entries.end());
if (newTo == newFrom || !newTo->second) {
newTo = m_entries.emplace_hint(newTo, to, false);
++newTo;
}
// else
// nothing to do really
// remove any intermediate entries, since all of the are excluded
m_entries.erase(newTo, newFrom);
m_wire.reset();
return *this;
}
Exclude&
Exclude::excludeAfter(const name::Component& from)
{
ExcludeMap::iterator newFrom = m_entries.lower_bound(from);
if (newFrom == m_entries.end() || !newFrom->second /*without ANY*/) {
bool isNewEntry = false;
std::tie(newFrom, isNewEntry) = m_entries.emplace(from, true);
if (!isNewEntry) {
// this means that the lower bound is equal to the item itself. So, just update ANY flag
newFrom->second = true;
}
}
// else
// nothing special if start of the range already exists with ANY flag set
// remove any intermediate node, since all of the are excluded
m_entries.erase(m_entries.begin(), newFrom);
m_wire.reset();
return *this;
}
std::ostream&
operator<<(std::ostream& os, const Exclude& exclude)
{
bool isFirst = true;
for (const Exclude::Entry& entry : exclude.m_entries | boost::adaptors::reversed) {
if (!entry.first.isNegInf) {
if (!isFirst)
os << ",";
entry.first.component.toUri(os);
isFirst = false;
}
if (entry.second) {
if (!isFirst)
os << ",";
os << "*";
isFirst = false;
}
}
return os;
}
std::string
Exclude::toUri() const
{
std::ostringstream os;
os << *this;
return os.str();
}
bool
Exclude::operator==(const Exclude& other) const
{
return m_entries == other.m_entries;
}
size_t
Exclude::size() const
{
return std::distance(begin(), end());
}
void
Exclude::clear()
{
m_entries.clear();
m_wire.reset();
}
Exclude::const_iterator::const_iterator(ExcludeMap::const_reverse_iterator it,
ExcludeMap::const_reverse_iterator rend)
: m_it(it)
, m_rend(rend)
{
this->update();
}
Exclude::const_iterator&
Exclude::const_iterator::operator++()
{
bool wasInRange = m_it->second;
++m_it;
if (wasInRange && m_it != m_rend) {
BOOST_ASSERT(m_it->second == false); // consecutive ranges should have been combined
++m_it; // skip over range high limit
}
this->update();
return *this;
}
Exclude::const_iterator
Exclude::const_iterator::operator++(int)
{
const_iterator i = *this;
this->operator++();
return i;
}
void
Exclude::const_iterator::update()
{
if (m_it == m_rend) {
return;
}
if (m_it->second) { // range
if (m_it->first.isNegInf) {
m_range.fromInfinity = true;
}
else {
m_range.fromInfinity = false;
m_range.from = m_it->first.component;
}
auto next = std::next(m_it);
if (next == m_rend) {
m_range.toInfinity = true;
}
else {
m_range.toInfinity = false;
m_range.to = next->first.component;
}
}
else { // single
BOOST_ASSERT(!m_it->first.isNegInf);
m_range.fromInfinity = m_range.toInfinity = false;
m_range.from = m_range.to = m_it->first.component;
}
}
} // namespace ndn