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