2009-07-17 19:27:04 +02:00
|
|
|
#ifndef _memorystream_h_
|
|
|
|
#define _memorystream_h_
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <string.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <sstream>
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class memory_streambuf: public std::streambuf {
|
|
|
|
private:
|
|
|
|
|
|
|
|
friend class memorystream;
|
|
|
|
|
|
|
|
//the current buffer
|
|
|
|
T* buf;
|
|
|
|
|
|
|
|
//the current allocated capacity of the buffer
|
|
|
|
size_t capacity;
|
|
|
|
|
|
|
|
//whether the sequence is owned by the stringbuf
|
|
|
|
bool myBuf;
|
|
|
|
|
|
|
|
//the logical length of the buffer
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
//the current 'write window' starting position within the buffer for writing.
|
|
|
|
size_t ww;
|
|
|
|
|
|
|
|
//a vector that we have been told to use
|
|
|
|
std::vector<T>* usevec;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
memory_streambuf(int _capacity)
|
|
|
|
: buf(new T[capacity=_capacity])
|
|
|
|
, myBuf(true)
|
|
|
|
, length(_capacity)
|
|
|
|
, ww(0)
|
|
|
|
, usevec(0)
|
|
|
|
{
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
memory_streambuf()
|
|
|
|
: buf(new T[capacity = 128])
|
|
|
|
, myBuf(true)
|
|
|
|
, length(0)
|
|
|
|
, ww(0)
|
|
|
|
, usevec(0)
|
|
|
|
{
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
//constructs a non-expandable streambuf around the provided buffer
|
|
|
|
memory_streambuf(T* usebuf, int buflength)
|
|
|
|
: buf(usebuf)
|
|
|
|
, myBuf(false)
|
|
|
|
, length(buflength)
|
|
|
|
, ww(0)
|
|
|
|
, usevec(0)
|
|
|
|
{
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
//constructs an expandable streambuf around the provided buffer
|
|
|
|
memory_streambuf(std::vector<T>* _usevec)
|
|
|
|
: capacity(_usevec->size())
|
|
|
|
, myBuf(false)
|
|
|
|
, length(_usevec->size())
|
|
|
|
, ww(0)
|
|
|
|
, usevec(_usevec)
|
|
|
|
{
|
|
|
|
if(length>0)
|
|
|
|
buf = &(*_usevec)[0];
|
|
|
|
else buf = 0;
|
|
|
|
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
~memory_streambuf()
|
|
|
|
{
|
|
|
|
//only cleanup if we own the seq
|
|
|
|
if(myBuf) delete[] buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
//the logical length of the buffer
|
|
|
|
size_t size()
|
|
|
|
{
|
|
|
|
sync();
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
//to avoid copying, rebuilds the provided vector and copies the streambuf contents into it
|
|
|
|
void toVector(std::vector<T>& out)
|
|
|
|
{
|
|
|
|
out.resize(length);
|
|
|
|
memcpy(&out[0],buf,length);
|
|
|
|
}
|
|
|
|
|
|
|
|
//maybe the compiler can avoid copying, but maybe not: returns a vector representing the current streambuf
|
|
|
|
std::vector<T> toVector()
|
|
|
|
{
|
|
|
|
return std::vector<T>(buf,buf+length);
|
|
|
|
}
|
|
|
|
|
|
|
|
//if the memorystream wraps a vector, the vector will be trimmed to the correct size,.
|
|
|
|
//you probably need to use this if you are using the vector wrapper
|
|
|
|
void trim()
|
|
|
|
{
|
|
|
|
if(!usevec) return;
|
|
|
|
usevec->resize(size());
|
|
|
|
}
|
|
|
|
|
|
|
|
//tells the current read or write position
|
|
|
|
std::streampos tell(std::ios::openmode which)
|
|
|
|
{
|
|
|
|
if(which == std::ios::in)
|
|
|
|
return tellRead();
|
|
|
|
else if(which == std::ios::out)
|
|
|
|
return tellWrite();
|
|
|
|
else return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//tells the current read position
|
|
|
|
std::streampos tellRead()
|
|
|
|
{
|
|
|
|
return gptr()-eback();
|
|
|
|
}
|
|
|
|
|
|
|
|
//tells the current write position
|
|
|
|
std::streampos tellWrite()
|
|
|
|
{
|
|
|
|
return pptr()-pbase() + ww;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sync()
|
|
|
|
{
|
|
|
|
dosync(-1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
T* getbuf()
|
|
|
|
{
|
|
|
|
sync();
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
//if we were provided a buffer, then calling this gives us ownership of it
|
|
|
|
void giveBuf() {
|
|
|
|
myBuf = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
void dosync(int c)
|
|
|
|
{
|
|
|
|
size_t wp = tellWrite();
|
|
|
|
size_t rp = tellRead();
|
|
|
|
|
|
|
|
//if we are supposed to insert a character..
|
|
|
|
if(c != -1)
|
|
|
|
{
|
|
|
|
buf[wp] = c;
|
|
|
|
wp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//the length is determined by the highest character that was ever inserted
|
|
|
|
length = std::max(length,wp);
|
|
|
|
|
|
|
|
//the write window advances to begin at the current write insertion point
|
|
|
|
ww = wp;
|
|
|
|
|
|
|
|
//set the new write and read windows
|
|
|
|
setp(buf+ww, buf + capacity);
|
|
|
|
setg(buf, buf+rp, buf + length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void expand(size_t upto)
|
|
|
|
{
|
|
|
|
if(!myBuf && !usevec)
|
|
|
|
throw new std::runtime_error("memory_streambuf is not expandable");
|
|
|
|
|
|
|
|
size_t newcapacity;
|
2009-07-18 08:19:04 +02:00
|
|
|
if(upto == 0)
|
2009-07-17 19:27:04 +02:00
|
|
|
newcapacity = capacity + capacity/2 + 2;
|
|
|
|
else
|
|
|
|
newcapacity = std::max(upto,capacity);
|
|
|
|
|
|
|
|
if(newcapacity == capacity) return;
|
|
|
|
|
|
|
|
//if we are supposed to use the vector, then do it now
|
|
|
|
if(usevec)
|
|
|
|
{
|
|
|
|
usevec->resize(newcapacity);
|
|
|
|
capacity = usevec->size();
|
|
|
|
buf = &(*usevec)[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//otherwise, manage our own buffer
|
|
|
|
T* newbuf = new T[newcapacity];
|
|
|
|
memcpy(newbuf,buf,capacity);
|
|
|
|
delete[] buf;
|
|
|
|
capacity = newcapacity;
|
|
|
|
buf = newbuf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
int overflow(int c)
|
|
|
|
{
|
|
|
|
expand(-1);
|
|
|
|
dosync(c);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::streambuf::pos_type seekpos(pos_type pos, std::ios::openmode which)
|
|
|
|
{
|
|
|
|
//extend if we are seeking the write cursor
|
|
|
|
if(which & std::ios_base::out)
|
|
|
|
expand(pos);
|
|
|
|
|
|
|
|
sync();
|
|
|
|
|
|
|
|
if(which & std::ios_base::in)
|
|
|
|
setg(buf, buf+pos, buf + length);
|
|
|
|
if(which & std::ios_base::out)
|
|
|
|
{
|
|
|
|
ww = pos;
|
|
|
|
setp(buf+pos, buf + capacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which)
|
|
|
|
{
|
|
|
|
switch(way) {
|
|
|
|
case std::ios::beg:
|
|
|
|
return seekpos(off, which);
|
|
|
|
case std::ios::cur:
|
|
|
|
return seekpos(tell(which)+off, which);
|
|
|
|
case std::ios::end:
|
|
|
|
return seekpos(length+off, which);
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
//an iostream that uses the memory_streambuf to effectively act much like a c# memorystream
|
|
|
|
//please call sync() after writing data if you want to read it back
|
|
|
|
class memorystream : public std::basic_iostream<char, std::char_traits<char> >
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
memorystream()
|
|
|
|
: std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
|
|
|
{}
|
|
|
|
|
|
|
|
memorystream(int size)
|
|
|
|
: std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
|
|
|
, streambuf(size)
|
|
|
|
{}
|
|
|
|
|
|
|
|
memorystream(char* usebuf, int buflength)
|
|
|
|
: std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
|
|
|
, streambuf(usebuf, buflength)
|
|
|
|
{}
|
|
|
|
|
|
|
|
memorystream(std::vector<char>* usevec)
|
|
|
|
: std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
|
|
|
, streambuf(usevec)
|
|
|
|
{}
|
|
|
|
|
|
|
|
//the underlying memory_streambuf
|
|
|
|
memory_streambuf<char> streambuf;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
size_t size() { return streambuf.size(); }
|
|
|
|
char* buf() { return streambuf.getbuf(); }
|
|
|
|
//flushes all the writing state and ensures the stream is ready for reading
|
|
|
|
void sync() { streambuf.sync(); }
|
|
|
|
//rewinds the cursors to offset 0
|
|
|
|
void rewind() { streambuf.seekpos(0,std::ios::in | std::ios::out); }
|
|
|
|
|
|
|
|
//if the memorystream wraps a vector, the vector will be trimmed to the correct size,.
|
|
|
|
//you probably need to use this if you are using the vector wrapper
|
|
|
|
void trim() { streambuf.trim(); }
|
|
|
|
|
|
|
|
void giveBuf() { streambuf.giveBuf(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|