name: Make use of naming conventions for segment, version, timestamp, and sequence number encoding

Change-Id: I99fe3965b2a4797bd14b6966b1d1d7d8fc530aef
Refs: #1761
diff --git a/src/name-component.cpp b/src/name-component.cpp
new file mode 100644
index 0000000..a045766
--- /dev/null
+++ b/src/name-component.cpp
@@ -0,0 +1,342 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 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 "name-component.hpp"
+
+#include "encoding/block-helpers.hpp"
+#include "encoding/encoding-buffer.hpp"
+#include "util/string-helper.hpp"
+
+namespace ndn {
+namespace name {
+
+Component::Component()
+  : Block(tlv::NameComponent)
+{
+}
+
+Component::Component(const Block& wire)
+  : Block(wire)
+{
+  if (type() != tlv::NameComponent)
+    throw Error("Cannot construct name::Component from not a NameComponent TLV wire block");
+}
+
+Component::Component(const ConstBufferPtr& buffer)
+  : Block(tlv::NameComponent, buffer)
+{
+}
+
+Component::Component(const Buffer& value)
+  : Block(dataBlock(tlv::NameComponent, value.buf(), value.size()))
+{
+}
+
+Component::Component(const uint8_t* value, size_t valueLen)
+  : Block(dataBlock(tlv::NameComponent, value, valueLen))
+{
+}
+
+Component::Component(const char* str)
+  : Block(dataBlock(tlv::NameComponent, str, ::strlen(str)))
+{
+}
+
+Component::Component(const std::string& str)
+  : Block(dataBlock(tlv::NameComponent, str.c_str(), str.size()))
+{
+}
+
+
+Component
+Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
+{
+  std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
+  trim(trimmedString);
+  std::string value = unescape(trimmedString);
+
+  if (value.find_first_not_of(".") == std::string::npos) {
+    // Special case for component of only periods.
+    if (value.size() <= 2)
+      // Zero, one or two periods is illegal.  Ignore this component.
+      throw Error("Illegal URI (name component cannot be . or ..)");
+    else
+      // Remove 3 periods.
+      return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
+  }
+  else
+    return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
+}
+
+
+void
+Component::toUri(std::ostream& result) const
+{
+  const uint8_t* value = this->value();
+  size_t valueSize = value_size();
+
+  bool gotNonDot = false;
+  for (size_t i = 0; i < valueSize; ++i) {
+    if (value[i] != 0x2e) {
+      gotNonDot = true;
+      break;
+    }
+  }
+  if (!gotNonDot) {
+    // Special case for component of zero or more periods.  Add 3 periods.
+    result << "...";
+    for (size_t i = 0; i < valueSize; ++i)
+      result << '.';
+  }
+  else {
+    // In case we need to escape, set to upper case hex and save the previous flags.
+    std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
+
+    for (size_t i = 0; i < valueSize; ++i) {
+      uint8_t x = value[i];
+      // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
+      if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
+          (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
+          x == 0x2e || x == 0x5f)
+        result << x;
+      else {
+        result << '%';
+        if (x < 16)
+          result << '0';
+        result << static_cast<uint32_t>(x);
+      }
+    }
+
+    // Restore.
+    result.flags(saveFlags);
+  }
+}
+
+std::string
+Component::toUri() const
+{
+  std::ostringstream os;
+  toUri(os);
+  return os.str();
+}
+
+uint64_t
+Component::toNumber() const
+{
+  /// \todo Check if Component is of tlv::NumberComponent type
+  return readNonNegativeInteger(*this);
+}
+
+uint64_t
+Component::toNumberWithMarker(uint8_t marker) const
+{
+  if (empty() || value()[0] != marker)
+    throw Error("Name component does not have the requested marker");
+
+  Buffer::const_iterator valueBegin = value_begin() + 1;
+  return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
+}
+
+uint64_t
+Component::toVersion() const
+{
+  return toNumberWithMarker(VERSION_MARKER);
+}
+
+uint64_t
+Component::toSegment() const
+{
+  return toNumberWithMarker(SEGMENT_MARKER);
+}
+
+uint64_t
+Component::toSegmentOffset() const
+{
+  return toNumberWithMarker(SEGMENT_OFFSET_MARKER);
+}
+
+time::system_clock::TimePoint
+Component::toTimestamp() const
+{
+  uint64_t value = toNumberWithMarker(TIMESTAMP_MARKER);
+  return time::getUnixEpoch() + time::microseconds(value);
+}
+
+uint64_t
+Component::toSequenceNumber() const
+{
+  return toNumberWithMarker(SEQUENCE_NUMBER_MARKER);
+}
+
+
+Component
+Component::fromNumber(uint64_t number)
+{
+  /// \todo Change to tlv::NumberComponent
+  return nonNegativeIntegerBlock(tlv::NameComponent, number);
+}
+
+Component
+Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
+{
+  EncodingEstimator estimator;
+
+  size_t valueLength = estimator.prependNonNegativeInteger(number);
+  valueLength += estimator.prependByteArray(&marker, 1);
+  size_t totalLength = valueLength;
+  totalLength += estimator.prependVarNumber(valueLength);
+  totalLength += estimator.prependVarNumber(tlv::NameComponent);
+
+  EncodingBuffer encoder(totalLength, 0);
+  encoder.prependNonNegativeInteger(number);
+  encoder.prependByteArray(&marker, 1);
+  encoder.prependVarNumber(valueLength);
+  encoder.prependVarNumber(tlv::NameComponent);
+
+  return encoder.block();
+}
+
+Component
+Component::fromVersion(uint64_t version)
+{
+  return fromNumberWithMarker(VERSION_MARKER, version);
+}
+
+Component
+Component::fromSegment(uint64_t segmentNo)
+{
+  return fromNumberWithMarker(SEGMENT_MARKER, segmentNo);
+}
+
+Component
+Component::fromSegmentOffset(uint64_t offset)
+{
+  return fromNumberWithMarker(SEGMENT_OFFSET_MARKER, offset);
+}
+
+Component
+Component::fromTimestamp(const time::system_clock::TimePoint& timePoint)
+{
+  using namespace time;
+  uint64_t value = duration_cast<microseconds>(timePoint - getUnixEpoch()).count();
+  return fromNumberWithMarker(TIMESTAMP_MARKER, value);
+}
+
+Component
+Component::fromSequenceNumber(uint64_t seqNo)
+{
+  return fromNumberWithMarker(SEQUENCE_NUMBER_MARKER, seqNo);
+}
+
+int
+Component::compare(const Component& other) const
+{
+  // Imitate ndn_Exclude_compareComponents.
+  if (value_size() < other.value_size())
+    return -1;
+  if (value_size() > other.value_size())
+    return 1;
+
+  if (value_size() == 0)
+    return 0;
+
+  // The components are equal length.  Just do a byte compare.
+  return std::memcmp(value(), other.value(), value_size());
+}
+
+Component
+Component::getSuccessor() const
+{
+  size_t totalLength = 0;
+  EncodingBuffer encoder(size() + 1, 1); // + 1 in case there is an overflow
+                                         // in unlikely case TLV length increases,
+                                         // EncodingBuffer will take care of that
+
+  bool isOverflow = true;
+  size_t i = value_size();
+  for (; isOverflow && i > 0; i--) {
+    uint8_t newValue = static_cast<uint8_t>((value()[i - 1] + 1) & 0xFF);
+    totalLength += encoder.prependByte(newValue);
+    isOverflow = (newValue == 0);
+  }
+  totalLength += encoder.prependByteArray(value(), i);
+
+  if (isOverflow) {
+    // new name components has to be extended
+    totalLength += encoder.appendByte(0);
+  }
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::NameComponent);
+
+  return encoder.block();
+}
+
+
+template<bool T>
+size_t
+Component::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+  if (value_size() > 0)
+    totalLength += block.prependByteArray(value(), value_size());
+  totalLength += block.prependVarNumber(value_size());
+  totalLength += block.prependVarNumber(tlv::NameComponent);
+  return totalLength;
+}
+
+template size_t
+Component::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+Component::wireEncode<false>(EncodingImpl<false>& block) const;
+
+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) = buffer.block();
+  return *this;
+}
+
+void
+Component::wireDecode(const Block& wire)
+{
+  if (wire.type() != tlv::NameComponent)
+    throw Error("name::Component::wireDecode called on not a NameComponent TLV wire block");
+
+  *this = wire;
+}
+
+} // namespace name
+} // namespace ndn