text: // bdlb_indexspan.h                                                   -*-C++-*-
  #ifndef INCLUDED_BDLB_INDEXSPAN
  #define INCLUDED_BDLB_INDEXSPAN
  
  #include <bsls_ident.h>
  BSLS_IDENT("$Id: $")
  
  //@PURPOSE: Provide a value-semantic attribute type for position and length.
  //
  //@CLASSES:
  //  bdlb::IndexSpan: value-semantic attribute type to represent position/length
  //
  //@SEE_ALSO: bdlb_indexspanutil, bdlb_indexspanstringutil
  //
  //@DESCRIPTION: This component provides a value-semantic, constrained
  // attribute type 'IndexSpan' that stores a position and a length.  The
  // constraint is that the sum of the two attributes must be representable by
  // the type 'bsl::size_t'.
  //
  ///Attributes
  ///----------
  //..
  //  Name                Type                  Default  Simple Constraints
  //  ------------------  --------------------  -------  ------------------
  //  position            IndexSpan::size_type  0        none
  //  length              IndexSpan::size_type  0        none
  //..
  //: o 'position': this span starts at this position in a sequence.
  //:
  //: o 'length': this span contains this many elements.
  //
  // For example, an 'IndexSpan' describing the middle name in the string
  // "James Tiberius Kirk" will have the position 6 (the 'T' of "Tiberius" is the
  // 7th character of the string) and the length 8 ("Tiberius is 8 characters
  // long).
  //
  // Note that the sum of the two attributes must be representable by the
  // 'bsl::size_t' type.
  //
  ///Usage
  ///-----
  // This section illustrates intended use of this component.
  //
  ///Example 1: A Parsed Path
  /// - - - - - - - - - - - -
  // Suppose that we work with file system paths and we frequently need the path,
  // the base name, the extension, and the filename, as well as the full path
  // itself.  Therefore we want to create a class that stores the full path and a
  // way to quickly access the individual parts.  (To keep things simple we will
  // make the example code work on POSIX-style paths so we do not need to deal
  // with the difference in path separators.)
  //
  // If we try the naive solution -- storing a 'bsl::string' of the full path and
  // 'bslstl::StringRef' for path, basename and extension -- the class
  // 'bslstl::StringRef' members will reference back into memory owned by the
  // object, and the result is the compiler-generated (and naively written) move
  // and copy operations will be broken.  In addition, explicitly implementing
  // move and copy operations is quite difficult and error prone.
  //
  // The simplest (most readable) solution to the problem is to store a position
  // and a length for the path, basename, and extension (rather than a
  // 'bslstl::StringRef') because that representation is independent of the
  // location of the memory for the string.  'IndexSpan' provides such a
  // representation, and allow us to implement our class using a simple copy and
  // move operations (or in other contexts, compiler supplied operations).
  //
  // First, we define the members and interface of the class.
  //..
  //  class ParsedPath {
  //    private:
  //      // DATA
  //      bsl::string     d_full;  // The full path
  //      bdlb::IndexSpan d_path;  // The path part if present, otherwise empty
  //      bdlb::IndexSpan d_base;  // The base name if present, otherwise empty
  //      bdlb::IndexSpan d_ext;   // The extension if present, otherwise empty
  //
  //    public:
  //      // CREATE
  //      explicit ParsedPath(const bslstl::StringRef&  full,
  //                          bslma::Allocator         *basicAllocator = 0);
  //          // Create a new 'ParsedPath' object by storing and parsing the
  //          // specified 'full' path.  Use the optionally specified
  //          // 'basicAllocator' to allocate memory.  If 'basicAllocator' is 0,
  //          // use the currently installed default allocator.
  //
  //      ParsedPath(const ParsedPath& original,
  //                 bslma::Allocator *basicAllocator = 0);
  //          // Create a new 'ParsedPath' object that stores the same parsed
  //          // path as the specified 'original'.  Use the optionally specified
  //          // 'basicAllocator' to allocate memory.  Use the currently
  //          // installed default allocator if 'basicAllocator' is zero.
  //
  //      // ACCESSORS
  //      const bsl::string& full() const;
  //          // Return a const reference to the full path parsed by this object.
  //
  //      bslstl::StringRef path() const;
  //          // Return a string reference to the path part.  Note that it may be
  //          // an empty string reference.
  //
  //      bslstl::StringRef base() const;
  //          // Return a string reference to the base name part.  Note that it
  //          // may be an empty string reference.
  //
  //      bslstl::StringRef ext()  const;
  //          // Return a string reference to the extension part.  Note that it
  //          // may be an empty string reference.
  //  };
  //..
  //  Next, to make the parsing code short and readable, we implement a helper
  //  function to create 'IndexSpan' objects from two positions.  (In practice we
  //  would use the higher level utility function
  //  'IndexSpanStringUtil::createFromPosition'.)
  //..
  //  bdlb::IndexSpan createFromPositions(bdlb::IndexSpan::size_type startPos,
  //                                      bdlb::IndexSpan::size_type endPos)
  //      // Return an 'IndexSpan' describing the specified '[startPos, endPos)'
  //      // positions.
  //  {
  //      return bdlb::IndexSpan(startPos, endPos - startPos);
  //  }
  //..
  //  Then, we implement the parsing constructor using the 'create' function.
  //..
  //  // CREATORS
  //  ParsedPath::ParsedPath(const bslstl::StringRef&  full,
  //                         bslma::Allocator         *basicAllocator)
  //  : d_full(full, basicAllocator)
  //  {
  //      typedef bsl::string::size_type Size;
  //
  //      static const Size npos = bsl::string::npos;
  //
  //      const Size slashPos = d_full.rfind('/');
  //      const Size dotPos   = d_full.rfind('.');
  //
  //      const bool dotIsPresent   = (dotPos   != npos);
  //      const bool slashIsPresent = (slashPos != npos);
  //
  //      const bool dotIsInPath = slashIsPresent && (dotPos < slashPos);
  //
  //      const bool isDotFile = dotIsPresent &&
  //                               dotPos == (slashIsPresent ? slashPos + 1 : 0);
  //
  //      const bool hasExtension = dotIsPresent && !dotIsInPath && !isDotFile;
  //      const bool hasPath      = slashIsPresent;
  //
  //      if (hasPath) {
  //          d_path = createFromPositions(0, slashPos + 1);
  //      }
  //
  //      d_base = createFromPositions(slashPos + 1,
  //                                   hasExtension ? dotPos : full.length());
  //
  //      if (hasExtension) {
  //          d_ext = createFromPositions(dotPos + 1, full.length());
  //      }
  //  }
  //..
  // Next, we implement the (now) simple copy constructor:
  //..
  //  ParsedPath::ParsedPath(const ParsedPath& original,
  //                         bslma::Allocator *basicAllocator)
  //  : d_full(original.d_full, basicAllocator)
  //  , d_path(original.d_path)
  //  , d_base(original.d_base)
  //  , d_ext(original.d_ext)
  //  {
  //  }
  //..
  //  Then, to make the accessors simple (and readable), we implement a helper
  //  function that creates a 'StringRef' from a 'StringRef' and an 'IndexSpan'.
  //  (In practice we would use the higher level utility function
  //  'IndexSpanStringUtil::bind'.)
  //..
  //  bslstl::StringRef bindSpan(const bslstl::StringRef& full,
  //                             const bdlb::IndexSpan&   part)
  //      // Return a string reference to the substring of the specified 'full'
  //      // string defined by the specified 'part'.
  //  {
  //      BSLS_ASSERT(part.position() <= full.length());
  //      BSLS_ASSERT(part.position() + part.length() <= full.length());
  //
  //      return bslstl::StringRef(full.data() + part.position(), part.length());
  //  }
  //..
  //  Next, we implement the accessors:
  //..
  //  // ACCESSORS
  //  bslstl::StringRef ParsedPath::base() const
  //  {
  //      return bindSpan(d_full, d_base);
  //  }
  //
  //  bslstl::StringRef ParsedPath::ext() const
  //  {
  //      return bindSpan(d_full, d_ext);
  //  }
  //
  //  const bsl::string& ParsedPath::full() const
  //  {
  //      return d_full;
  //  }
  //
  //  bslstl::StringRef ParsedPath::path() const
  //  {
  //      return bindSpan(d_full, d_path);
  //  }
  //..
  // Finally, we verify that the resulting class is copied properly.  We do that
  // by determining that the original and the copy object has equal but distinct
  // strings and that the (other) accessors return references into those strings.
  //..
  //  ParsedPath aPath("path/path/basename.extension");
  //  ParsedPath theCopy(aPath);
  //
  //  assert(aPath.full()        == theCopy.full());
  //  assert(aPath.full().data() != theCopy.full().data());
  //
  //  assert(aPath.path()        == theCopy.path());
  //  assert(aPath.path().data() != theCopy.path().data());
  //  assert(aPath.path().data() >= aPath.full().data());
  //  assert(aPath.path().data() + aPath.path().length() <=
  //                                aPath.full().data() + aPath.full().length());
  //
  //  assert(aPath.base()        == theCopy.base());
  //  assert(aPath.base().data() != theCopy.base().data());
  //  assert(aPath.base().data() >= aPath.full().data());
  //  assert(aPath.base().data() + aPath.base().length() <=
  //                                aPath.full().data() + aPath.full().length());
  //
  //  assert(aPath.ext()        == theCopy.ext());
  //  assert(aPath.ext().data() != theCopy.ext().data());
  //  assert(aPath.ext().data() >= aPath.full().data());
  //  assert(aPath.ext().data() + aPath.ext().length() <=
  //                                aPath.full().data() + aPath.full().length());
  //..
  
  #include <bdlscm_version.h>
  
  #include <bdlb_printmethods.h> // 'bdlb::HasPrintMethod'
  
  #include <bslh_hash.h>
  
  #include <bslmf_istriviallycopyable.h>
  #include <bslmf_nestedtraitdeclaration.h>
  
  #include <bsls_assert.h>
  
  #include <bsl_cstddef.h>
  #include <bsl_iosfwd.h>
  #include <bsl_limits.h>
  
  namespace BloombergLP {
  namespace bdlb {
  
                              // ===============
                              // class IndexSpan
                              // ===============
  
  class IndexSpan {
      // A constrained attribute type that represents a position and a length.
      // The constraint is that the sum of the position and length attributes
      // must be representable by the 'bsl::size_t' type.
  
    public:
      // TYPES
      typedef bsl::size_t size_type;
          // The type of the position and length attributes.
  
    private:
      // DATA
      size_type d_position;  // The spans starts at this position
      size_type d_length;    // The span has this length
  
    public:
      // TRAITS
      BSLMF_NESTED_TRAIT_DECLARATION(IndexSpan, bsl::is_trivially_copyable);
      BSLMF_NESTED_TRAIT_DECLARATION(IndexSpan, bdlb::HasPrintMethod);
  
      // CREATORS
      IndexSpan();
          // Create an 'IndexSpan' object with position and length of 0.
  
      IndexSpan(size_type position, size_type length);
          // Create an 'IndexSpan' object with the specified 'position' and
          // 'length'.  The behavior is undefined unless
          // 'position <= bsl::numeric_limits<size_t>::max() - length'.
  
      // IndexSpan(const IndexSpan& original) = default;
          // Create an index span having the value of the specified 'original'
          // index span.  Note that this trivial copy constructor is generated by
          // the compiler.
  
      // ~IndexSpan() = default;
          // Destroy this index span object.  Note that this trivial destructor
          // is generated by the compiler.
  
      // MANIPULATORS
      // IndexSpan& operator=(const IndexSpan& rhs) = default;
          // Assign to this index span the value of the specified 'rhs' index
          // span, and return a reference providing modifiable access to this
          // object.  Note that this trivial assignment operation is generated by
          // the compiler.
  
      // ACCESSORS
      bool isEmpty() const;
          // Return 'true' if the length attribute is 0; and return 'false'
          // otherwise.
  
      size_type length() const;
          // Return the length attribute.
  
      size_type position() const;
          // Return the position attribute.
  
                                    // Aspects
  
      bsl::ostream& print(bsl::ostream& stream,
                          int           level          = 0,
                          int           spacesPerLevel = 4) const;
          // Format this object to the specified output 'stream' at the (absolute
          // value of) the optionally specified indentation 'level' and return a
          // reference to 'stream'.  If 'level' is specified, optionally specify
          // 'spacesPerLevel', the number of spaces per indentation level for
          // this and all of its nested objects.  If 'level' is negative,
          // suppress indentation of the first line.  If 'spacesPerLevel' is
          // negative, format the entire output on one line, suppressing all but
          // the initial indentation (as governed by 'level').  If 'stream' is
          // not valid on entry, this operation has no effect.
  };
  
  // FREE OPERATORS
  bool operator==(const IndexSpan& lhs, const IndexSpan& rhs);
      // Return 'true' if the specified 'lhs' and 'rhs' index span objects have
      // the same value, and 'false' otherwise.  Two index span objects have the
      // same value if their position and length attributes compare equal.
  
  bool operator!=(const IndexSpan& lhs, const IndexSpan& rhs);
      // Return 'true' if the specified 'lhs' and 'rhs' index span objects do not
      // have the same value, and 'false' otherwise.  Two index span objects do
      // not have the same value if either their position or length attributes
      // do not compare equal.
  
  bsl::ostream& operator<<(bsl::ostream& stream, const IndexSpan& object);
      // Write the value of the specified 'object' to the specified output
      // 'stream' in a single-line format, and return a reference to 'stream'.
      // If 'stream' is not valid on entry, this operation has no effect.  Note
      // that this human-readable format is not fully specified, can change
      // without notice, and is logically equivalent to:
      //..
      //  print(stream, 0, -1);
      //..
  
  template <class HASH_ALGORITHM>
  void hashAppend(HASH_ALGORITHM& hashAlgorithm, const IndexSpan& object);
      // Invoke the specified 'hashAlgorithm' on the attributes of the specified
      // 'object'.
  
  // ============================================================================
  //                             INLINE DEFINITIONS
  // ============================================================================
  
                              // ---------------
                              // class IndexSpan
                              // ---------------
  
  // CREATORS
  inline
  IndexSpan::IndexSpan()
  : d_position(0u)
  , d_length(0u)
  {
  }
  
  inline
  IndexSpan::IndexSpan(size_type position, size_type length)
  : d_position(position)
  , d_length(length)
  {
      BSLS_ASSERT(position <= bsl::numeric_limits<size_t>::max() - length);
  }
  
  // ACCESSORS
  inline
  bool IndexSpan::isEmpty() const
  {
      return 0 == d_length;
  }
  
  inline
  IndexSpan::size_type IndexSpan::length() const
  {
      return d_length;
  }
  
  inline
  IndexSpan::size_type IndexSpan::position() const
  {
      return d_position;
  }
  
  }  // close package namespace
  
  // FREE OPERATORS
  inline
  bool bdlb::operator==(const IndexSpan& lhs, const IndexSpan& rhs)
  {
      return lhs.position() == rhs.position()
          && lhs.length()   == rhs.length();
  }
  
  inline
  bool bdlb::operator!=(const IndexSpan& lhs, const IndexSpan& rhs)
  {
      return !(lhs == rhs);
  }
  
  inline
  bsl::ostream& bdlb::operator<<(bsl::ostream&    stream,
                                 const IndexSpan& object)
  {
      return object.print(stream, 0, -1);
  }
  
  template <class HASH_ALGORITHM>
  inline
  void bdlb::hashAppend(HASH_ALGORITHM& hashAlgorithm, const IndexSpan& object)
  {
      const IndexSpan::size_type pos = object.position();
      const IndexSpan::size_type len = object.length();
  
      using bslh::hashAppend;
      hashAppend(hashAlgorithm, pos);
      hashAppend(hashAlgorithm, len);
  }
  
  }  // close enterprise namespace
  
  #endif
  
  // ----------------------------------------------------------------------------
  // Copyright 2018 Bloomberg Finance L.P.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  // ----------------------------- END-OF-FILE ----------------------------------
  