static_assert( __cplusplus > 2020'99 ); #pragma once #include #include #include #include #include #include #include "StackableStreambuf.h" #include "StreamState.h" namespace Alepha::Hydrogen::IOStreams ::detail:: LineTrackingStreambuf_m { inline namespace exports {} struct TrackLines_params {}; namespace exports { struct LineTrackingStreambuf; using TrackLines= IOStreams::PushStack< TrackLines_params >; std::int64_t getLineNumber( std::ios_base &ios ); auto setLineNumber( std::int64_t baseLine ); } inline StreamState< LineTrackingStreambuf * > tracker{ []{ return nullptr; } }; struct exports::LineTrackingStreambuf : public virtual StackableStreambuf, public virtual std::streambuf { private: std::int64_t baseLine= 1; std::vector< std::int64_t > lineStarts; char *bufBase= nullptr; std::int64_t current() const { return gptr() - bufBase; } std::int64_t index() const { return std::lower_bound( begin( lineStarts ), end( lineStarts ), current() ) - begin( lineStarts ); } public: explicit LineTrackingStreambuf( std::ios &is ) : StackableStreambuf( is ) { tracker.get( is )= this; } void setLineNumber( const std::int64_t baseLine ) { this->baseLine= baseLine - index(); } std::int64_t getLineNumber() const { return baseLine + index(); } void writeChar( char ch ) override { throw "Unimpl"; } void drain() override { throw "Unimpl"; } int underflow() override { baseLine+= lineStarts.size(); lineStarts.clear(); const auto rv= forwardUnderflow(); if( rv == EOF ) return rv; assume_underlying(); const std::string_view view{ gptr(), egptr() }; bufBase= gptr(); for( std::int64_t i= 0; i < view.size(); ++i ) { if( view.at( i ) == '\n' ) lineStarts.push_back( i + 1 ); } return rv; } }; inline std::int64_t exports::getLineNumber( std::ios_base &ios ) { auto thisTracker= tracker.get( ios ); if( thisTracker == nullptr ) return -1; return thisTracker->getLineNumber(); } #if 0 struct Setter { const std::int64_t baseLine; friend std::istream & operator >> ( std::istream &is, const Setter &s ) { auto thisTracker= tracker.get( is ); if( thisTracker != nullptr ) { thisTracker->setLineNumber( s.baseLine ); } return is; } }; inline auto exports::setLineNumber( const std::int64_t baseLine ) { return Setter{ baseLine }; } #endif inline void build_streambuf( std::istream &is, TrackLines && ) { new LineTrackingStreambuf{ is }; } } namespace Alepha::Hydrogen::IOStreams::inline exports::inline LineTrackingStreambuf_m { using namespace detail::LineTrackingStreambuf_m::exports; }