]> git.tdb.fi Git - libs/core.git/blob - source/buffered.cpp
4cd5cb4d0eed742c81357cedd634895793dae7f2
[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 "error.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         in_buf(new char[buf_size]),
19         in_ptr(in_buf),
20         in_avail(0),
21         out_buf(new char[buf_size]),
22         out_used(0)
23 {
24         mode=below.get_mode();
25         below.signal_closing.connect(sigc::mem_fun(this, &Buffered::below_closing));
26 }
27
28 unsigned Buffered::put(char c)
29 {
30         if(out_used<buf_size)
31         {
32                 out_buf[out_used++]=c;
33                 return 1;
34         }
35         else
36                 return do_write(&c, 1);
37 }
38
39 void Buffered::flush()
40 {
41         if(out_used==0)
42                 return;
43
44         unsigned len=below.write(out_buf, out_used);
45         if(len<out_used)
46         {
47                 memmove(out_buf, out_buf+len, out_used-len);
48                 out_used-=len;
49                 throw Exception("Couldn't flush all data");
50         }
51         out_used=0;
52 }
53
54 bool Buffered::getline(std::string &line)
55 {
56         for(unsigned i=0; i<in_avail; ++i)
57                 if(in_ptr[i]=='\n')
58                 {
59                         line.assign(in_ptr, i);
60                         in_ptr+=i+1;
61                         in_avail-=i+1;
62                         return true;
63                 }
64
65         return Base::getline(line);
66 }
67
68 int Buffered::get()
69 {
70         if(in_avail>0)
71         {
72                 --in_avail;
73                 return *in_ptr++;
74         }
75
76         char c;
77         if(do_read(&c, 1)==0)
78                 return -1;
79         return static_cast<unsigned char>(c);
80 }
81
82 Handle Buffered::get_event_handle()
83 {
84         throw Exception("Buffered doesn't support events");
85 }
86
87 Buffered::~Buffered()
88 {
89         try
90         {
91                 flush();
92         }
93         catch(...)
94         { }
95
96         delete[] in_buf;
97         delete[] out_buf;
98 }
99
100 void Buffered::below_closing()
101 {
102         flush();
103 }
104
105 unsigned Buffered::do_write(const char *buf, unsigned size)
106 {
107         if(out_used+size<buf_size)
108         {
109                 // All data fits in the buffer
110                 memcpy(out_buf+out_used, buf, size);
111                 out_used+=size;
112
113                 return size;
114         }
115         else
116         {
117                 int ret=0;
118                 bool ok=true;
119
120                 while(size>0)
121                 {
122                         // XXX sub-obtimal - should try to write directly from input first
123                         // Fill the buffer and pass it on
124                         unsigned head=min(buf_size-out_used, size);
125
126                         memcpy(out_buf+out_used, buf, head);
127                         out_used+=head;
128
129                         buf+=head;
130                         size-=head;
131                         ret+=head;
132
133                         if(!ok) break;
134
135                         unsigned len=below.write(out_buf, out_used);
136
137                         ok=(len==out_used);
138                         if(ok)
139                                 out_used=0;
140                         else
141                         {
142                                 memmove(out_buf, out_buf+len, buf_size-len);
143                                 out_used=buf_size-len;
144                         }
145                 }
146
147                 return ret;
148         }
149 }
150
151 unsigned Buffered::do_read(char *buf, unsigned size)
152 {
153         if(size<=in_avail)
154         {
155                 // The request can be served from the buffer
156                 memcpy(buf, in_ptr, size);
157                 in_ptr+=size;
158                 in_avail-=size;
159
160                 return size;
161         }
162         else
163         {
164                 // Use whatever is left in the buffer
165                 memcpy(buf, in_ptr, in_avail);
166
167                 buf+=in_avail;
168                 size-=in_avail;
169                 int ret=in_avail;
170
171                 in_ptr=in_buf;
172                 in_avail=0;
173
174                 if(size>=buf_size)
175                         ret+=below.read(buf, size);
176                 else
177                 {
178                         // Read more data into the buffer
179                         while(size>0)
180                         {
181                                 in_avail=below.read(in_buf, buf_size);
182                                 if(in_avail==0)
183                                 {
184                                         eof_flag=true;
185                                         break;
186                                 }
187
188                                 unsigned head=min(size, in_avail);
189                                 memcpy(buf, in_buf, head);
190                                 buf+=head;
191                                 size-=head;
192
193                                 in_ptr=in_buf+head;
194                                 in_avail-=head;
195                                 ret+=head;
196                         }
197                 }
198
199                 return ret;
200         }
201 }
202
203 } // namespace IO
204 } // namespace Msp