]> git.tdb.fi Git - libs/core.git/blob - buffered.cpp
2836e4bbbaeb879de07be26bf5fba2ecea8fcad9
[libs/core.git] / 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::set_block(bool)
36 {
37         throw logic_error("Buffered::set_block");
38 }
39
40 void Buffered::set_inherit(bool)
41 {
42         throw logic_error("Buffered::set_block");
43 }
44
45 void Buffered::flush()
46 {
47         if(cur_op==M_WRITE)
48         {
49                 unsigned used = end-begin;
50                 if(used)
51                 {
52                         unsigned len = below.write(begin, used);
53
54                         begin = end = buf;
55
56                         if(len<used)
57                                 throw runtime_error("Couldn't flush all data");
58                 }
59         }
60         else if(cur_op==M_READ)
61                 begin = end = buf;
62 }
63
64 unsigned Buffered::do_write(const char *data, unsigned size)
65 {
66         set_op(M_WRITE);
67
68         if(end+size<buf+buf_size)
69         {
70                 // All data fits in buffer with whatever is already there
71                 memcpy(end, data, size);
72                 end += size;
73
74                 return size;
75         }
76         else
77         {
78                 // Clear the buffer to make more room
79                 flush();
80
81                 if(size<buf_size)
82                 {
83                         // Put new data in the buffer to wait for more
84                         memcpy(end, data, size);
85                         end += size;
86
87                         return size;
88                 }
89                 else
90                         // New data still doesn't fit in the buffer, so write it directly
91                         return below.write(data, size);
92         }
93 }
94
95 unsigned Buffered::do_read(char *data, unsigned size)
96 {
97         set_op(M_READ);
98
99         if(begin+size<=end)
100         {
101                 // The request can be served from the buffer
102                 memcpy(data, begin, size);
103                 begin += size;
104
105                 eof_flag = (below.eof() && begin==end);
106
107                 return size;
108         }
109         else
110         {
111                 // Give out whatever is in the buffer already
112                 memcpy(data, begin, end-begin);
113                 unsigned ret = end-begin;
114                 begin = end = buf;
115
116                 data += ret;
117                 size -= ret;
118
119                 if(size<buf_size)
120                 {
121                         // Fill the buffer and serve the rest of the request from it
122                         unsigned len = below.read(end, buf+buf_size-end);
123                         end += len;
124
125                         len = min(static_cast<unsigned>(end-begin), size);
126                         memcpy(data, begin, len);
127                         begin += len;
128                         ret += len;
129                 }
130                 else
131                         // Read the rest directly from the underlying object
132                         ret += below.read(data, size);
133
134                 eof_flag = (below.eof() && begin==end);
135
136                 return ret;
137         }
138 }
139
140 unsigned Buffered::put(char c)
141 {
142         set_op(M_WRITE);
143
144         if(end<buf+buf_size)
145         {
146                 *end++ = c;
147                 return 1;
148         }
149         else
150                 return do_write(&c, 1);
151 }
152
153 bool Buffered::getline(std::string &line)
154 {
155         set_op(M_READ);
156
157         for(char *i=begin; i!=end; ++i)
158                 if(*i=='\n')
159                 {
160                         line.assign(begin, i-begin);
161                         begin = i+1;
162                         return true;
163                 }
164
165         return Base::getline(line);
166 }
167
168 int Buffered::get()
169 {
170         set_op(M_READ);
171
172         if(begin<end)
173                 return static_cast<unsigned char>(*begin++);
174
175         char c;
176         if(do_read(&c, 1)==0)
177                 return -1;
178         return static_cast<unsigned char>(c);
179 }
180
181 const Handle &Buffered::get_handle(Mode)
182 {
183         throw logic_error("Buffered::get_handle");
184 }
185
186 void Buffered::set_op(Mode op)
187 {
188         if(op!=cur_op)
189                 flush();
190         cur_op = op;
191 }
192
193 unsigned Buffered::get_current_size() const
194 {
195         return end-begin;
196 }
197
198 } // namespace IO
199 } // namespace Msp