blob: 90922716ad733414ce012e8321199d82cce9d9c6 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013-2023 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 Jeff Thompson <jefft0@remap.ucla.edu>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
*/
#include "ndn-cxx/name-component.hpp"
#include "ndn-cxx/impl/name-component-types.hpp"
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <boost/logic/tribool.hpp>
namespace ndn::name {
static Convention g_conventionEncoding = Convention::TYPED;
static Convention g_conventionDecoding = Convention::EITHER;
Convention
getConventionEncoding() noexcept
{
return g_conventionEncoding;
}
void
setConventionEncoding(Convention convention)
{
switch (convention) {
case Convention::MARKER:
case Convention::TYPED:
g_conventionEncoding = convention;
break;
default:
NDN_THROW(std::invalid_argument("Unknown naming convention"));
}
}
Convention
getConventionDecoding() noexcept
{
return g_conventionDecoding;
}
void
setConventionDecoding(Convention convention)
{
g_conventionDecoding = convention;
}
static bool
canDecodeMarkerConvention() noexcept
{
return (to_underlying(g_conventionDecoding) & to_underlying(Convention::MARKER)) != 0;
}
static bool
canDecodeTypedConvention() noexcept
{
return (to_underlying(g_conventionDecoding) & to_underlying(Convention::TYPED)) != 0;
}
////////////////////////////////////////////////////////////////////////////////
Component::Component(uint32_t type)
: Block(type)
{
ensureValid();
}
Component::Component(const Block& wire)
: Block(wire)
{
ensureValid();
}
Component::Component(uint32_t type, ConstBufferPtr buffer)
: Block(type, std::move(buffer))
{
ensureValid();
}
Component::Component(uint32_t type, span<const uint8_t> value)
: Block(makeBinaryBlock(type, value))
{
ensureValid();
}
Component::Component(std::string_view str)
: Block(makeStringBlock(tlv::GenericNameComponent, str))
{
}
void
Component::ensureValid() const
{
if (type() < tlv::NameComponentMin || type() > tlv::NameComponentMax) {
NDN_THROW(Error("TLV-TYPE " + std::to_string(type()) + " is not a valid NameComponent"));
}
getComponentTypeTable().get(type()).check(*this);
}
static Component
parseUriEscapedValue(uint32_t type, std::string_view input)
{
std::ostringstream oss;
unescape(oss, input);
std::string value = oss.str();
if (value.find_first_not_of('.') == std::string::npos) { // all periods
if (value.size() < 3) {
NDN_THROW(Component::Error("Illegal URI (name component cannot be . or ..)"));
}
return Component(type, {reinterpret_cast<const uint8_t*>(value.data()), value.size() - 3});
}
return Component(type, {reinterpret_cast<const uint8_t*>(value.data()), value.size()});
}
Component
Component::fromEscapedString(std::string_view input)
{
size_t equalPos = input.find('=');
if (equalPos == std::string_view::npos) {
return parseUriEscapedValue(tlv::GenericNameComponent, input);
}
auto typePrefix = input.substr(0, equalPos);
auto type = std::strtoul(typePrefix.data(), nullptr, 10);
if (type >= tlv::NameComponentMin && type <= tlv::NameComponentMax &&
std::to_string(type) == typePrefix) {
return parseUriEscapedValue(static_cast<uint32_t>(type), input.substr(equalPos + 1));
}
auto ct = getComponentTypeTable().findByUriPrefix(typePrefix);
if (ct == nullptr) {
NDN_THROW(Error("Unknown TLV-TYPE '" + std::string(typePrefix) + "' in NameComponent URI"));
}
return ct->parseAltUriValue(input.substr(equalPos + 1));
}
static bool
wantAltUri(UriFormat format)
{
static const auto wantAltEnv = []() -> boost::tribool {
const char* env = std::getenv("NDN_NAME_ALT_URI");
if (env == nullptr)
return boost::indeterminate;
else if (env[0] == '0')
return false;
else if (env[0] == '1')
return true;
else
return boost::indeterminate;
}();
if (format == UriFormat::ENV_OR_CANONICAL) {
static const bool wantAlt = boost::indeterminate(wantAltEnv) ? false : bool(wantAltEnv);
return wantAlt;
}
else if (format == UriFormat::ENV_OR_ALTERNATE) {
static const bool wantAlt = boost::indeterminate(wantAltEnv) ? true : bool(wantAltEnv);
return wantAlt;
}
else {
return format == UriFormat::ALTERNATE;
}
}
void
Component::toUri(std::ostream& os, UriFormat format) const
{
if (wantAltUri(format)) {
getComponentTypeTable().get(type()).writeUri(os, *this);
}
else {
ComponentType().writeUri(os, *this);
}
}
std::string
Component::toUri(UriFormat format) const
{
std::ostringstream os;
toUri(os, format);
return os.str();
}
////////////////////////////////////////////////////////////////////////////////
bool
Component::isNumber() const noexcept
{
return value_size() == 1 || value_size() == 2 ||
value_size() == 4 || value_size() == 8;
}
bool
Component::isNumberWithMarker(uint8_t marker) const noexcept
{
return (value_size() == 2 || value_size() == 3 ||
value_size() == 5 || value_size() == 9) && value()[0] == marker;
}
bool
Component::isSegment() const noexcept
{
return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_MARKER)) ||
(canDecodeTypedConvention() && type() == tlv::SegmentNameComponent && isNumber());
}
bool
Component::isByteOffset() const noexcept
{
return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_OFFSET_MARKER)) ||
(canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent && isNumber());
}
bool
Component::isVersion() const noexcept
{
return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(VERSION_MARKER)) ||
(canDecodeTypedConvention() && type() == tlv::VersionNameComponent && isNumber());
}
bool
Component::isTimestamp() const noexcept
{
return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(TIMESTAMP_MARKER)) ||
(canDecodeTypedConvention() && type() == tlv::TimestampNameComponent && isNumber());
}
bool
Component::isSequenceNumber() const noexcept
{
return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEQUENCE_NUMBER_MARKER)) ||
(canDecodeTypedConvention() && type() == tlv::SequenceNumNameComponent && isNumber());
}
////////////////////////////////////////////////////////////////////////////////
uint64_t
Component::toNumber() const
{
if (!isNumber())
NDN_THROW(Error("Name component does not have NonNegativeInteger value"));
return readNonNegativeInteger(*this);
}
uint64_t
Component::toNumberWithMarker(uint8_t marker) const
{
if (!isNumberWithMarker(marker))
NDN_THROW(Error("Name component does not have the requested marker "
"or the value is not a NonNegativeInteger"));
auto valueBegin = value_begin() + 1;
return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
}
uint64_t
Component::toSegment() const
{
if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
return toNumberWithMarker(SEGMENT_MARKER);
}
if (canDecodeTypedConvention() && type() == tlv::SegmentNameComponent) {
return toNumber();
}
NDN_THROW(Error("Not a Segment component"));
}
uint64_t
Component::toByteOffset() const
{
if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
return toNumberWithMarker(SEGMENT_OFFSET_MARKER);
}
if (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent) {
return toNumber();
}
NDN_THROW(Error("Not a ByteOffset component"));
}
uint64_t
Component::toVersion() const
{
if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
return toNumberWithMarker(VERSION_MARKER);
}
if (canDecodeTypedConvention() && type() == tlv::VersionNameComponent) {
return toNumber();
}
NDN_THROW(Error("Not a Version component"));
}
time::system_clock::time_point
Component::toTimestamp() const
{
uint64_t value = 0;
if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
value = toNumberWithMarker(TIMESTAMP_MARKER);
}
else if (canDecodeTypedConvention() && type() == tlv::TimestampNameComponent) {
value = toNumber();
}
else {
NDN_THROW(Error("Not a Timestamp component"));
}
return time::getUnixEpoch() + time::microseconds(value);
}
uint64_t
Component::toSequenceNumber() const
{
if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
return toNumberWithMarker(SEQUENCE_NUMBER_MARKER);
}
if (canDecodeTypedConvention() && type() == tlv::SequenceNumNameComponent) {
return toNumber();
}
NDN_THROW(Error("Not a SequenceNumber component"));
}
////////////////////////////////////////////////////////////////////////////////
Component
Component::fromNumber(uint64_t number, uint32_t type)
{
return Component(makeNonNegativeIntegerBlock(type, number));
}
Component
Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
{
EncodingEstimator estimator;
size_t valueLength = estimator.prependNonNegativeInteger(number);
valueLength += estimator.prependBytes({marker});
size_t totalLength = valueLength;
totalLength += estimator.prependVarNumber(valueLength);
totalLength += estimator.prependVarNumber(tlv::GenericNameComponent);
EncodingBuffer encoder(totalLength, 0);
encoder.prependNonNegativeInteger(number);
encoder.prependBytes({marker});
encoder.prependVarNumber(valueLength);
encoder.prependVarNumber(tlv::GenericNameComponent);
return Component(encoder.block());
}
Component
Component::fromSegment(uint64_t segmentNo)
{
return g_conventionEncoding == Convention::MARKER ?
fromNumberWithMarker(SEGMENT_MARKER, segmentNo) :
fromNumber(segmentNo, tlv::SegmentNameComponent);
}
Component
Component::fromByteOffset(uint64_t offset)
{
return g_conventionEncoding == Convention::MARKER ?
fromNumberWithMarker(SEGMENT_OFFSET_MARKER, offset) :
fromNumber(offset, tlv::ByteOffsetNameComponent);
}
Component
Component::fromVersion(uint64_t version)
{
return g_conventionEncoding == Convention::MARKER ?
fromNumberWithMarker(VERSION_MARKER, version) :
fromNumber(version, tlv::VersionNameComponent);
}
Component
Component::fromTimestamp(const time::system_clock::time_point& timePoint)
{
uint64_t value = time::duration_cast<time::microseconds>(timePoint - time::getUnixEpoch()).count();
return g_conventionEncoding == Convention::MARKER ?
fromNumberWithMarker(TIMESTAMP_MARKER, value) :
fromNumber(value, tlv::TimestampNameComponent);
}
Component
Component::fromSequenceNumber(uint64_t seqNo)
{
return g_conventionEncoding == Convention::MARKER ?
fromNumberWithMarker(SEQUENCE_NUMBER_MARKER, seqNo) :
fromNumber(seqNo, tlv::SequenceNumNameComponent);
}
////////////////////////////////////////////////////////////////////////////////
bool
Component::isImplicitSha256Digest() const noexcept
{
return type() == tlv::ImplicitSha256DigestComponent && value_size() == util::Sha256::DIGEST_SIZE;
}
bool
Component::isParametersSha256Digest() const noexcept
{
return type() == tlv::ParametersSha256DigestComponent && value_size() == util::Sha256::DIGEST_SIZE;
}
////////////////////////////////////////////////////////////////////////////////
int
Component::compare(const Component& other) const
{
if (this->hasWire() && other.hasWire()) {
// In the common case where both components have wire encoding,
// it's more efficient to simply compare the wire encoding.
// This works because lexical order of TLV encoding happens to be
// the same as canonical order of the value.
return std::memcmp(data(), other.data(), std::min(size(), other.size()));
}
int cmpType = type() - other.type();
if (cmpType != 0)
return cmpType;
int cmpSize = value_size() - other.value_size();
if (cmpSize != 0)
return cmpSize;
if (empty())
return 0;
return std::memcmp(value(), other.value(), value_size());
}
Component
Component::getSuccessor() const
{
auto [isOverflow, successor] = getComponentTypeTable().get(type()).getSuccessor(*this);
if (!isOverflow) {
return successor;
}
uint32_t type = this->type() + 1;
auto value = getComponentTypeTable().get(type).getMinValue();
return {type, value};
}
template<encoding::Tag TAG>
size_t
Component::wireEncode(EncodingImpl<TAG>& encoder) const
{
size_t totalLength = 0;
if (value_size() > 0) {
totalLength += encoder.prependBytes(value_bytes());
}
totalLength += encoder.prependVarNumber(value_size());
totalLength += encoder.prependVarNumber(type());
return totalLength;
}
NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(Component);
const Block&
Component::wireEncode() const
{
if (this->hasWire())
return *this;
EncodingEstimator estimator;
size_t estimatedSize = wireEncode(estimator);
EncodingBuffer buffer(estimatedSize, 0);
wireEncode(buffer);
const_cast<Component*>(this)->wireDecode(buffer.block());
return *this;
}
void
Component::wireDecode(const Block& wire)
{
*this = Component(wire);
// validity check is done within Component(const Block& wire)
}
} // namespace ndn::name