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