+#include <cstring>
+#include "buffered.h"
+#include "except.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+Buffered::Buffered(Base &b, unsigned s):
+ below(b),
+ buf_size(s),
+ buf(new char[buf_size]),
+ begin(buf),
+ end(buf),
+ cur_op(M_NONE)
+{
+ mode = below.get_mode();
+ below.signal_flush_required.connect(sigc::mem_fun(this, &Buffered::flush));
+}
+
+Buffered::~Buffered()
+{
+ try
+ {
+ flush();
+ }
+ catch(...)
+ { }
+
+ delete[] buf;
+}
+
+void Buffered::flush()
+{
+ if(cur_op==M_WRITE)
+ {
+ unsigned used = end-begin;
+ if(used)
+ {
+ unsigned len = below.write(begin, used);
+
+ begin=end = buf;
+
+ if(len<used)
+ throw Exception("Couldn't flush all data");
+ }
+ }
+ else if(cur_op==M_READ)
+ begin=end = buf;
+}
+
+unsigned Buffered::do_write(const char *data, unsigned size)
+{
+ set_op(M_WRITE);
+
+ if(end+size<buf+buf_size)
+ {
+ // All data fits in buffer with whatever is already there
+ memcpy(end, data, size);
+ end += size;
+
+ return size;
+ }
+ else
+ {
+ // Clear the buffer to make more room
+ flush();
+
+ if(size<buf_size)
+ {
+ // Put new data in the buffer to wait for more
+ memcpy(end, data, size);
+ end += size;
+
+ return size;
+ }
+ else
+ // New data still doesn't fit in the buffer, so write it directly
+ return below.write(data, size);
+ }
+}
+
+unsigned Buffered::do_read(char *data, unsigned size)
+{
+ set_op(M_READ);
+
+ if(begin+size<=end)
+ {
+ // The request can be served from the buffer
+ memcpy(data, begin, size);
+ begin += size;
+
+ eof_flag = (below.eof() && begin==end);
+
+ return size;
+ }
+ else
+ {
+ // Give out whatever is in the buffer already
+ memcpy(data, begin, end-begin);
+ unsigned ret = end-begin;
+ begin=end = buf;
+
+ data += ret;
+ size -= ret;
+
+ if(size<buf_size)
+ {
+ // Fill the buffer and serve the rest of the request from it
+ unsigned len = below.read(end, buf+buf_size-end);
+ end += len;
+
+ len = min(static_cast<unsigned>(end-begin), size);
+ memcpy(data, begin, len);
+ begin += len;
+ ret += len;
+ }
+ else
+ // Read the rest directly from the underlying object
+ ret += below.read(data, size);
+
+ eof_flag = (below.eof() && begin==end);
+
+ return ret;
+ }
+}
+
+unsigned Buffered::put(char c)
+{
+ set_op(M_WRITE);
+
+ if(end<buf+buf_size)
+ {
+ *end++ = c;
+ return 1;
+ }
+ else
+ return do_write(&c, 1);
+}
+
+bool Buffered::getline(std::string &line)
+{
+ set_op(M_READ);
+
+ for(char *i=begin; i!=end; ++i)
+ if(*i=='\n')
+ {
+ line.assign(begin, i-begin);
+ begin = i+1;
+ return true;
+ }
+
+ return Base::getline(line);
+}
+
+int Buffered::get()
+{
+ set_op(M_READ);
+
+ if(begin<end)
+ return static_cast<unsigned char>(*begin++);
+
+ char c;
+ if(do_read(&c, 1)==0)
+ return -1;
+ return static_cast<unsigned char>(c);
+}
+
+void Buffered::set_op(Mode op)
+{
+ if(op!=cur_op)
+ flush();
+ cur_op = op;
+}
+
+unsigned Buffered::get_current_size() const
+{
+ return end-begin;
+}
+
+Handle Buffered::get_event_handle()
+{
+ throw Exception("Buffered doesn't support events");
+}
+
+} // namespace IO
+} // namespace Msp