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