blob: 19091f40ce8317453195ab66f8bb441c1542f1e9 [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);
}
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
{
const_iterator lb = m_entries.lower_bound(comp);
return lb != end() && // lb==end() means 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)"));
}
iterator newFrom = m_entries.lower_bound(from);
if (newFrom == 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
iterator newTo = m_entries.lower_bound(to);
BOOST_ASSERT(newTo != 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)
{
iterator newFrom = m_entries.lower_bound(from);
if (newFrom == 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;
}
void
Exclude::clear()
{
m_entries.clear();
m_wire.reset();
}
std::ostream&
operator<<(std::ostream& os, const Exclude& exclude)
{
bool isFirst = true;
for (const Exclude::Entry& entry : exclude | 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
{
if (empty() && other.empty())
return true;
if (empty() || other.empty())
return false;
return wireEncode() == other.wireEncode();
}
} // namespace ndn