/** @file span.hpp **/ #pragma once #include "xo/indentlog/scope.hpp" #include #include #include namespace xo { namespace scm { /** @class span compression/span.hpp * * @brief A contiguous range of characters, without ownership. * * @tparam CharT type for elements referred to by this span. **/ template class span { public: /** @defgroup span-type-traits span type traits **/ ///@{ /** typealias for span size (in units of CharT) **/ using size_type = std::uint64_t; ///@} public: /** @defgroup span-ctors span constructors **/ ///@{ /** Create span for the contiguous memory range [@p lo, @p hi) **/ span(CharT * lo, CharT * hi) : lo_{lo}, hi_{hi} {} /** Create a null span (i.e. with null @p lo, @p hi pointers) * A null span can be concatenated with any other span * without triggering matching-endpoint asserts. **/ static span make_null() { return span(nullptr, nullptr); } /** @brief create span for C-style string @p cstr **/ static span from_cstr(const CharT * cstr) { CharT * lo = cstr; CharT * hi = cstr ? cstr + strlen(cstr) : nullptr; return span(lo, hi); } /** @brief create span from std::string @p str **/ static span from_string(const std::string& str) { CharT * lo = &(*str.begin()); CharT * hi = &(*str.end()); return span(lo, hi); } /** @brief concatenate two contiguous spans */ static span concat(const span & span1, const span & span2) { if (span1.is_null()) return span2; if (span2.is_null()) return span1; if (span1.hi() != span2.lo()) { scope log(XO_DEBUG(true)); log && log(xtag("span1.hi", (void*)span1.hi()), xtag("span2.lo", (void*)span2.lo())); } assert(span1.hi() == span2.lo()); CharT * lo = span1.lo(); CharT * hi = span2.hi(); return span(lo, hi); } ///@} /** @defgroup span-access-methods **/ ///@{ CharT * lo() const { return lo_; } /* get member span::lo_ */ CharT * hi() const { return hi_; } /* get member span::hi_ */ ///@} /** @defgroup span-general-methods **/ ///@{ /** Create new span over supplied type, * with identical (possibly misaligned) endpoints. * * @warning * 1. New span uses exactly the same memory addresses. * Endpoint pointers may not be aligned. * 2. Implementation assumes code compiled with * @code -fno-strict-aliasing @endcode enabled. * * @tparam OtherT element type for new span **/ template span cast() const { return span(reinterpret_cast(lo_), reinterpret_cast(hi_)); } /** @brief create span including the first @p z members of this span. **/ span prefix(size_type z) const { return span(lo_, lo_ + z); } /** @brief create span representing prefix up to (but not including) @p *p **/ span prefix_upto(CharT * p) const { if (p <= hi_) return span(lo_, p); else return span(lo_, hi_); } /** @brief create span with first @p z members of this span removed **/ span after_prefix(size_type z) const { if (lo_ + z > hi_) z = hi_ - lo_; return span(lo_ + z, hi_); } /** @brief create span with @p prefix of this span removed **/ span after_prefix(const span & prefix) const { assert(prefix.lo() == lo_); if (prefix.lo() != lo_) { throw std::runtime_error ("after_prefix: expected prefix of this span"); } return after_prefix(prefix.size()); } /** Create span starting with position @p p. * Does boundary checking; will return empty span if @p p is outside @c [lo_,hi) **/ span suffix_from(CharT * p) const { if ((lo_ <= p) && (p <= hi_)) return span(p, hi_); else return span(hi_, hi_); } /** true iff this span is null. distinct from empty. **/ bool is_null() const { return lo_ == nullptr && hi_ == nullptr; } /** true iff this span is empty (comprises 0 elements). **/ bool empty() const { return lo_ == hi_; } /** report the number of elements (of type CharT) in this span. **/ size_type size() const { return hi_ - lo_; } /** increase extent of this spans to include @p x. * Requires @c hi() == @c x.lo() **/ span & operator+=(const span & x) { if (hi_ == x.lo_) { hi_ = x.hi_; } else { assert(false); } return *this; } /** print representation for this span on stream @p os **/ void print(std::ostream & os) const { os << ""; } ///@} private: /** @defgroup span-instance-vars **/ ///@{ /** start of span. Span comprises memory address between @p lo (inclusive) and @p hi (exclusive) **/ CharT * lo_ = nullptr; /** @brief end of span. Span comprises memory address between @p lo (inclusive) and @p hi (exclusive) **/ CharT * hi_ = nullptr; ///@} }; /*span*/ /** @defgroup span-operators **/ ///@{ /** compare spans for equality. * Two spans are equal iff both endpoints match exactly. **/ template inline bool operator==(const span & lhs, const span & rhs) { return ((lhs.lo() == rhs.lo()) && (lhs.hi() == rhs.hi())); } /** compare spans for inequality. * Two spans are unequal if either paired endpoint differs. **/ template inline bool operator!=(const span & lhs, const span & rhs) { return ((lhs.lo() != rhs.lo()) || (lhs.hi() != rhs.hi())); } /** print a summary of @p x on stream @p os. Intended for diagnostics **/ template inline std::ostream & operator<<(std::ostream & os, const span & x) { x.print(os); return os; } ///@} } /*namespace scm*/ namespace print { template class printspan_impl { public: printspan_impl(xo::scm::span x) : span_{x} {} xo::scm::span span_; }; template printspan_impl printspan(const xo::scm::span& span) { return printspan_impl(span); } template inline std::ostream & operator<< (std::ostream & os, const printspan_impl & x) { for (const CharT * p = x.span_.lo(); p < x.span_.hi(); ++p) os << *p; return os; } } } /*namespace xo*/