351 lines
9.6 KiB
C++
351 lines
9.6 KiB
C++
// stream_shared_iterator.hpp
|
|
// Copyright (c) 2010-2012 Ben Hanson (http://www.benhanson.net/)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file licence_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#ifndef LEXERTL_STREAM_SHARED_ITERATOR_H
|
|
#define LEXERTL_STREAM_SHARED_ITERATOR_H
|
|
|
|
#include <algorithm>
|
|
// memcpy
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <list>
|
|
#include <math.h>
|
|
#include "runtime_error.hpp"
|
|
#include "size_t.hpp"
|
|
#include <vector>
|
|
|
|
namespace lexertl
|
|
{
|
|
template<typename char_type>
|
|
class basic_stream_shared_iterator
|
|
{
|
|
public:
|
|
typedef std::basic_istream<char_type> istream;
|
|
typedef std::forward_iterator_tag iterator_category;
|
|
typedef std::size_t difference_type;
|
|
typedef char_type value_type;
|
|
typedef char_type *pointer;
|
|
typedef char_type &reference;
|
|
|
|
basic_stream_shared_iterator () :
|
|
_master (false),
|
|
_live (false),
|
|
_index (shared::npos ()),
|
|
_shared (0)
|
|
{
|
|
}
|
|
|
|
basic_stream_shared_iterator (istream &stream_,
|
|
const std::size_t buff_size_ = 1024,
|
|
const std::size_t increment_ = 1024) :
|
|
_master (true),
|
|
_live (false),
|
|
_index (shared::npos ()),
|
|
// For exception safety don't call new yet
|
|
_shared (0)
|
|
{
|
|
// Safe to call potentially throwing new now.
|
|
_shared = new shared (stream_, buff_size_, increment_);
|
|
++_shared->_ref_count;
|
|
_iter = _shared->_clients.insert (_shared->_clients.end (), this);
|
|
}
|
|
|
|
basic_stream_shared_iterator (const basic_stream_shared_iterator &rhs_) :
|
|
_master (false),
|
|
_live (false),
|
|
_index (rhs_._master ? rhs_._shared->lowest () : rhs_._index),
|
|
_shared (rhs_._shared)
|
|
{
|
|
if (_shared)
|
|
{
|
|
// New copy of an iterator.
|
|
// The assumption is that any copy must be live
|
|
// even if the rhs is not (otherwise we will never
|
|
// have a record of the start of the current range!)
|
|
++_shared->_ref_count;
|
|
_iter = _shared->_clients.insert (_shared->_clients.end (), this);
|
|
_live = true;
|
|
}
|
|
}
|
|
|
|
~basic_stream_shared_iterator ()
|
|
{
|
|
if (_shared)
|
|
{
|
|
--_shared->_ref_count;
|
|
_shared->erase (this);
|
|
|
|
if (_shared->_ref_count == 0)
|
|
{
|
|
delete _shared;
|
|
_shared = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
basic_stream_shared_iterator &operator =
|
|
(const basic_stream_shared_iterator &rhs_)
|
|
{
|
|
if (this != &rhs_)
|
|
{
|
|
_master = false;
|
|
_index = rhs_._master ? rhs_._shared->lowest () : rhs_._index;
|
|
|
|
if (_live && !rhs_._live)
|
|
{
|
|
_shared->erase (this);
|
|
|
|
if (!rhs_._shared)
|
|
{
|
|
--_shared->_ref_count;
|
|
}
|
|
}
|
|
else if (!_live && rhs_._live)
|
|
{
|
|
rhs_._iter = rhs_._shared->_clients.insert (rhs_._shared->
|
|
_clients.end (), this);
|
|
|
|
if (!_shared)
|
|
{
|
|
++rhs_._shared->_ref_count;
|
|
}
|
|
}
|
|
|
|
_live = rhs_._live;
|
|
_shared = rhs_._shared;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool operator == (const basic_stream_shared_iterator &rhs_) const
|
|
{
|
|
return _index == rhs_._index &&
|
|
(_shared == rhs_._shared ||
|
|
(_index == shared::npos () || rhs_._index == shared::npos ()) &&
|
|
(!_shared || !rhs_._shared));
|
|
}
|
|
|
|
bool operator != (const basic_stream_shared_iterator &rhs_) const
|
|
{
|
|
return !(*this == rhs_);
|
|
}
|
|
|
|
const char_type &operator * ()
|
|
{
|
|
check_master ();
|
|
return _shared->_buffer[_index];
|
|
}
|
|
|
|
basic_stream_shared_iterator &operator ++ ()
|
|
{
|
|
check_master ();
|
|
++_index;
|
|
update_state ();
|
|
return *this;
|
|
}
|
|
|
|
basic_stream_shared_iterator operator ++ (int)
|
|
{
|
|
basic_stream_shared_iterator iter_ = *this;
|
|
|
|
check_master ();
|
|
++_index;
|
|
update_state ();
|
|
return iter_;
|
|
}
|
|
|
|
private:
|
|
class shared
|
|
{
|
|
public:
|
|
std::size_t _ref_count;
|
|
typedef std::vector<char_type> char_vector;
|
|
typedef std::list<basic_stream_shared_iterator *> iter_list;
|
|
istream &_stream;
|
|
std::size_t _increment;
|
|
std::size_t _len;
|
|
char_vector _buffer;
|
|
iter_list _clients;
|
|
|
|
shared (istream &stream_, const std::size_t buff_size_,
|
|
const std::size_t increment_) :
|
|
_ref_count (0),
|
|
_increment (increment_),
|
|
_stream (stream_)
|
|
{
|
|
_buffer.resize (buff_size_);
|
|
_stream.read (&_buffer.front (), _buffer.size ());
|
|
_len = static_cast<std::size_t>(_stream.gcount ());
|
|
}
|
|
|
|
bool reload_buffer ()
|
|
{
|
|
const std::size_t lowest_ = lowest ();
|
|
std::size_t read_ = 0;
|
|
|
|
if (lowest_ == 0)
|
|
{
|
|
// Resize buffer
|
|
const std::size_t old_size_ = _buffer.size ();
|
|
const std::size_t new_size_ = old_size_ + _increment;
|
|
|
|
_buffer.resize (new_size_);
|
|
_stream.read (&_buffer.front () + old_size_, _increment);
|
|
read_ = static_cast<std::size_t>(_stream.gcount ());
|
|
|
|
if (read_)
|
|
{
|
|
read_ += old_size_;
|
|
_len = read_;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Some systems have memcpy in namespace std
|
|
using namespace std;
|
|
const size_t start_ = _buffer.size () - lowest_;
|
|
const size_t len_ = _buffer.size () - start_;
|
|
|
|
memcpy (&_buffer.front (), &_buffer[lowest_], start_ *
|
|
sizeof (char_type));
|
|
_stream.read (&_buffer.front () + start_, len_);
|
|
read_ = static_cast<size_t>(_stream.gcount ());
|
|
subtract (lowest_);
|
|
|
|
if (read_)
|
|
{
|
|
read_ += start_;
|
|
_len = read_;
|
|
}
|
|
else
|
|
{
|
|
_len = highest ();
|
|
}
|
|
}
|
|
|
|
return read_ != 0;
|
|
}
|
|
|
|
void erase (basic_stream_shared_iterator *ptr_)
|
|
{
|
|
if (ptr_->_iter != _clients.end ())
|
|
{
|
|
_clients.erase (ptr_->_iter);
|
|
ptr_->_iter = _clients.end ();
|
|
}
|
|
}
|
|
|
|
std::size_t lowest () const
|
|
{
|
|
std::size_t lowest_ = npos ();
|
|
typename iter_list::const_iterator iter_ = _clients.begin ();
|
|
typename iter_list::const_iterator end_ = _clients.end ();
|
|
|
|
for (; iter_ != end_; ++iter_)
|
|
{
|
|
const basic_stream_shared_iterator *ptr_ = *iter_;
|
|
|
|
if (ptr_->_index < lowest_)
|
|
{
|
|
lowest_ = ptr_->_index;
|
|
}
|
|
}
|
|
|
|
if (lowest_ == npos ())
|
|
{
|
|
lowest_ = 0;
|
|
}
|
|
|
|
return lowest_;
|
|
}
|
|
|
|
std::size_t highest () const
|
|
{
|
|
std::size_t highest_ = 0;
|
|
typename iter_list::const_iterator iter_ = _clients.begin ();
|
|
typename iter_list::const_iterator end_ = _clients.end ();
|
|
|
|
for (; iter_ != end_; ++iter_)
|
|
{
|
|
const basic_stream_shared_iterator *ptr_ = *iter_;
|
|
|
|
if (ptr_->_index != npos () && ptr_->_index > highest_)
|
|
{
|
|
highest_ = ptr_->_index;
|
|
}
|
|
}
|
|
|
|
return highest_;
|
|
}
|
|
|
|
void subtract (const std::size_t lowest_)
|
|
{
|
|
typename iter_list::iterator iter_ = _clients.begin ();
|
|
typename iter_list::iterator end_ = _clients.end ();
|
|
|
|
for (; iter_ != end_; ++iter_)
|
|
{
|
|
basic_stream_shared_iterator *ptr_ = *iter_;
|
|
|
|
if (ptr_->_index != npos ())
|
|
{
|
|
ptr_->_index -= lowest_;
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::size_t npos ()
|
|
{
|
|
return static_cast<std::size_t>(~0);
|
|
}
|
|
|
|
private:
|
|
shared &operator = (const shared &rhs_);
|
|
};
|
|
|
|
bool _master;
|
|
bool _live;
|
|
std::size_t _index;
|
|
shared *_shared;
|
|
mutable typename shared::iter_list::iterator _iter;
|
|
|
|
void check_master ()
|
|
{
|
|
if (!_shared)
|
|
{
|
|
throw runtime_error ("Cannot manipulate null (end) "
|
|
"stream_shared_iterators.");
|
|
}
|
|
|
|
if (_master)
|
|
{
|
|
_master = false;
|
|
_live = true;
|
|
_index = _shared->lowest ();
|
|
}
|
|
}
|
|
|
|
void update_state ()
|
|
{
|
|
if (_index >= _shared->_len)
|
|
{
|
|
if (!_shared->reload_buffer ())
|
|
{
|
|
_shared->erase (this);
|
|
_index = shared::npos ();
|
|
_live = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
typedef basic_stream_shared_iterator<char> stream_shared_iterator;
|
|
typedef basic_stream_shared_iterator<wchar_t> wstream_shared_iterator;
|
|
}
|
|
|
|
#endif
|