Rewrite Buffered to support read-write buffering correctly
[libs/core.git] / source / buffered.cpp
1 /* $Id$
2
3 This file is part of libmspio
4 Copyright © 2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7 #include "buffered.h"
8 #include "except.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace IO {
14
15 Buffered::Buffered(Base &b, unsigned s):
16         below(b),
17         buf_size(s),
18         buf(new char[buf_size]),
19         begin(buf),
20         end(buf),
21         cur_op(M_NONE)
22 {
23         mode=below.get_mode();
24         below.signal_flush_required.connect(sigc::mem_fun(this, &Buffered::flush));
25 }
26
27 unsigned Buffered::put(char c)
28 {
29         set_op(M_WRITE);
30
31         if(end<buf+buf_size)
32         {
33                 *end++=c;
34                 return 1;
35         }
36         else
37                 return do_write(&c, 1);
38 }
39
40 void Buffered::flush()
41 {
42         if(cur_op==M_WRITE)
43         {
44                 unsigned used=end-begin;
45                 if(used)
46                 {
47                         unsigned len=below.write(begin, used);
48
49                         begin=end=buf;
50
51                         if(len<used)
52                                 throw Exception("Couldn't flush all data");
53                 }
54         }
55         else if(cur_op==M_READ)
56                 begin=end=buf;
57 }
58
59 bool Buffered::getline(std::string &line)
60 {
61         set_op(M_READ);
62
63         for(char *i=begin; i!=end; ++i)
64                 if(*i=='\n')
65                 {
66                         line.assign(begin, i-begin);
67                         begin=i+1;
68                         return true;
69                 }
70
71         return Base::getline(line);
72 }
73
74 int Buffered::get()
75 {
76         set_op(M_READ);
77
78         if(begin<end)
79                 return static_cast<unsigned char>(*begin++);
80
81         char c;
82         if(do_read(&c, 1)==0)
83                 return -1;
84         return static_cast<unsigned char>(c);
85 }
86
87 Handle Buffered::get_event_handle()
88 {
89         throw Exception("Buffered doesn't support events");
90 }
91
92 unsigned Buffered::get_current_size() const
93 {
94         return end-begin;
95 }
96
97 Buffered::~Buffered()
98 {
99         try
100         {
101                 flush();
102         }
103         catch(...)
104         { }
105
106         delete[] buf;
107 }
108
109 void Buffered::set_op(Mode op)
110 {
111         if(op!=cur_op)
112                 flush();
113         cur_op=op;
114 }
115
116 unsigned Buffered::do_write(const char *data, unsigned size)
117 {
118         set_op(M_WRITE);
119
120         if(end+size<buf+buf_size)
121         {
122                 // All data fits in buffer with whatever is already there
123                 memcpy(end, data, size);
124                 end+=size;
125
126                 return size;
127         }
128         else
129         {
130                 // Clear the buffer to make more room
131                 flush();
132
133                 if(size<buf_size)
134                 {
135                         // Put new data in the buffer to wait for more
136                         memcpy(end, data, size);
137                         end+=size;
138
139                         return size;
140                 }
141                 else
142                         // New data still doesn't fit in the buffer, so write it directly
143                         return below.write(data, size);
144         }
145 }
146
147 unsigned Buffered::do_read(char *data, unsigned size)
148 {
149         set_op(M_READ);
150
151         if(begin+size<=end)
152         {
153                 // The request can be served from the buffer
154                 memcpy(data, begin, size);
155                 begin+=size;
156
157                 eof_flag=(below.eof() && begin==end);
158
159                 return size;
160         }
161         else
162         {
163                 // Give out whatever is in the buffer already
164                 memcpy(data, begin, end-begin);
165                 unsigned ret=end-begin;
166                 begin=end=buf;
167
168                 data+=ret;
169                 size-=ret;
170
171                 if(size<buf_size)
172                 {
173                         // Fill the buffer and serve the rest of the request from it
174                         unsigned len=below.read(end, buf+buf_size-end);
175                         end+=len;
176
177                         len=min(static_cast<unsigned>(end-begin), size);
178                         memcpy(data, begin, len);
179                         begin+=len;
180                         ret+=len;
181                 }
182                 else
183                         // Read the rest directly from the underlying object
184                         ret+=below.read(data, size);
185
186                 eof_flag=(below.eof() && begin==end);
187
188                 return ret;
189         }
190 }
191
192 } // namespace IO
193 } // namespace Msp