1
0
forked from Alepha/Alepha
Files
Alepha/IOStreams/LineTrackingStreambuf.h

139 lines
2.8 KiB
C++

static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <cstdint>
#include <vector>
#include <iostream>
#include <string_view>
#include <algorithm>
#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;
}