9c9900f8b1e19f2bd9142214627b959ab7bb44ac
[profile/ivi/pulseaudio.git] / src / pulsecore / ioline.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5  
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2 of the License,
9   or (at your option) any later version.
10  
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <pulse/xmalloc.h>
33
34 #include <pulsecore/core-error.h>
35 #include <pulsecore/log.h>
36
37 #include "ioline.h"
38
39 #define BUFFER_LIMIT (64*1024)
40 #define READ_SIZE (1024)
41
42 struct pa_ioline {
43     pa_iochannel *io;
44     pa_defer_event *defer_event;
45     pa_mainloop_api *mainloop;
46     int ref;
47     int dead;
48
49     char *wbuf;
50     size_t wbuf_length, wbuf_index, wbuf_valid_length;
51
52     char *rbuf;
53     size_t rbuf_length, rbuf_index, rbuf_valid_length;
54
55     void (*callback)(pa_ioline*io, const char *s, void *userdata);
56     void *userdata;
57
58     int defer_close;
59 };
60
61 static void io_callback(pa_iochannel*io, void *userdata);
62 static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
63
64 pa_ioline* pa_ioline_new(pa_iochannel *io) {
65     pa_ioline *l;
66     assert(io);
67     
68     l = pa_xnew(pa_ioline, 1);
69     l->io = io;
70     l->dead = 0;
71
72     l->wbuf = NULL;
73     l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
74
75     l->rbuf = NULL;
76     l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
77
78     l->callback = NULL;
79     l->userdata = NULL;
80     l->ref = 1;
81
82     l->mainloop = pa_iochannel_get_mainloop_api(io);
83
84     l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
85     l->mainloop->defer_enable(l->defer_event, 0);
86
87     l->defer_close = 0;
88     
89     pa_iochannel_set_callback(io, io_callback, l);
90     
91     return l;
92 }
93
94 static void ioline_free(pa_ioline *l) {
95     assert(l);
96
97     if (l->io)
98         pa_iochannel_free(l->io);
99
100     if (l->defer_event)
101         l->mainloop->defer_free(l->defer_event);
102
103     pa_xfree(l->wbuf);
104     pa_xfree(l->rbuf);
105     pa_xfree(l);
106 }
107
108 void pa_ioline_unref(pa_ioline *l) {
109     assert(l);
110     assert(l->ref >= 1);
111
112     if ((--l->ref) <= 0)
113         ioline_free(l);
114 }
115
116 pa_ioline* pa_ioline_ref(pa_ioline *l) {
117     assert(l);
118     assert(l->ref >= 1);
119
120     l->ref++;
121     return l;
122 }
123
124 void pa_ioline_close(pa_ioline *l) {
125     assert(l);
126     assert(l->ref >= 1);
127
128     l->dead = 1;
129     
130     if (l->io) {
131         pa_iochannel_free(l->io);
132         l->io = NULL;
133     }
134
135     if (l->defer_event) {
136         l->mainloop->defer_free(l->defer_event);
137         l->defer_event = NULL;
138     }
139
140     if (l->callback)
141         l->callback = NULL;
142 }
143
144 void pa_ioline_puts(pa_ioline *l, const char *c) {
145     size_t len;
146     
147     assert(l);
148     assert(l->ref >= 1);
149     assert(c);
150
151     if (l->dead)
152         return;
153     
154     len = strlen(c);
155     if (len > BUFFER_LIMIT - l->wbuf_valid_length)
156         len = BUFFER_LIMIT - l->wbuf_valid_length;
157
158     if (len) {
159         assert(l->wbuf_length >= l->wbuf_valid_length);
160         
161         /* In case the allocated buffer is too small, enlarge it. */
162         if (l->wbuf_valid_length + len > l->wbuf_length) {
163             size_t n = l->wbuf_valid_length+len;
164             char *new = pa_xmalloc(n);
165             if (l->wbuf) {
166                 memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
167                 pa_xfree(l->wbuf);
168             }
169             l->wbuf = new;
170             l->wbuf_length = n;
171             l->wbuf_index = 0;
172         } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
173             
174             /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
175             memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
176             l->wbuf_index = 0;
177         }
178         
179         assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
180         
181         /* Append the new string */
182         memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
183         l->wbuf_valid_length += len;
184
185         l->mainloop->defer_enable(l->defer_event, 1);
186     }
187 }
188
189 void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
190     assert(l);
191     assert(l->ref >= 1);
192     
193     l->callback = callback;
194     l->userdata = userdata;
195 }
196
197 static void failure(pa_ioline *l, int process_leftover) {
198     assert(l);
199     assert(l->ref >= 1);
200     assert(!l->dead);
201
202     if (process_leftover && l->rbuf_valid_length > 0) {
203         /* Pass the last missing bit to the client */
204
205         if (l->callback) {
206             char *p = pa_xstrndup(l->rbuf+l->rbuf_index, l->rbuf_valid_length);
207             l->callback(l, p, l->userdata);
208             pa_xfree(p);
209         }
210     }
211
212     if (l->callback) {
213         l->callback(l, NULL, l->userdata);
214         l->callback = NULL;
215     }
216     
217     pa_ioline_close(l);
218 }
219
220 static void scan_for_lines(pa_ioline *l, size_t skip) {
221     assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
222
223     while (!l->dead && l->rbuf_valid_length > skip) {
224         char *e, *p;
225         size_t m;
226         
227         if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
228             break;
229
230         *e = 0;
231     
232         p = l->rbuf + l->rbuf_index;
233         m = strlen(p);
234
235         l->rbuf_index += m+1;
236         l->rbuf_valid_length -= m+1;
237
238         /* A shortcut for the next time */
239         if (l->rbuf_valid_length == 0)
240             l->rbuf_index = 0;
241
242         if (l->callback)
243             l->callback(l, p, l->userdata);
244
245         skip = 0;
246     }
247
248     /* If the buffer became too large and still no newline was found, drop it. */
249     if (l->rbuf_valid_length >= BUFFER_LIMIT)
250         l->rbuf_index = l->rbuf_valid_length = 0;
251 }
252
253 static int do_write(pa_ioline *l);
254
255 static int do_read(pa_ioline *l) {
256     assert(l && l->ref >= 1);
257
258     while (!l->dead && pa_iochannel_is_readable(l->io)) {
259         ssize_t r;
260         size_t len;
261
262         len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
263         
264         /* Check if we have to enlarge the read buffer */
265         if (len < READ_SIZE) {
266             size_t n = l->rbuf_valid_length+READ_SIZE;
267             
268             if (n >= BUFFER_LIMIT)
269                 n = BUFFER_LIMIT;
270             
271             if (l->rbuf_length >= n) {
272                 /* The current buffer is large enough, let's just move the data to the front */
273                 if (l->rbuf_valid_length)
274                     memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
275             } else {
276                 /* Enlarge the buffer */
277                 char *new = pa_xmalloc(n);
278                 if (l->rbuf_valid_length)
279                     memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
280                 pa_xfree(l->rbuf);
281                 l->rbuf = new;
282                 l->rbuf_length = n;
283             }
284             
285             l->rbuf_index = 0;
286         }
287         
288         len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
289         
290         assert(len >= READ_SIZE);
291         
292         /* Read some data */
293         if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
294             if (r < 0) {
295                 pa_log(__FILE__": read(): %s", pa_cstrerror(errno));
296                 failure(l, 0);
297             } else
298                 failure(l, 1);
299             
300             return -1;
301         }
302         
303         l->rbuf_valid_length += r;
304         
305         /* Look if a line has been terminated in the newly read data */
306         scan_for_lines(l, l->rbuf_valid_length - r);
307     }
308         
309     return 0;
310 }
311
312 /* Try to flush the buffer */
313 static int do_write(pa_ioline *l) {
314     ssize_t r;
315     assert(l && l->ref >= 1);
316
317     while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
318         
319         if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
320             pa_log(__FILE__": write(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
321             failure(l, 0);
322             return -1;
323         }
324         
325         l->wbuf_index += r;
326         l->wbuf_valid_length -= r;
327         
328         /* A shortcut for the next time */
329         if (l->wbuf_valid_length == 0)
330             l->wbuf_index = 0;
331     }
332         
333     return 0;
334 }
335
336 /* Try to flush read/write data */
337 static void do_work(pa_ioline *l) {
338     assert(l);
339     assert(l->ref >= 1);
340
341     pa_ioline_ref(l);
342
343     l->mainloop->defer_enable(l->defer_event, 0);
344     
345     if (!l->dead)
346         do_read(l);
347
348     if (!l->dead)
349         do_write(l);
350
351     if (l->defer_close && !l->wbuf_valid_length)
352         failure(l, 1);
353
354     pa_ioline_unref(l);
355 }
356
357 static void io_callback(pa_iochannel*io, void *userdata) {
358     pa_ioline *l = userdata;
359     assert(io && l && l->ref >= 1);
360
361     do_work(l);
362 }
363
364 static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
365     pa_ioline *l = userdata;
366     assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
367
368     do_work(l);
369 }
370
371 void pa_ioline_defer_close(pa_ioline *l) {
372     assert(l);
373     assert(l->ref >= 1);
374     
375     l->defer_close = 1;
376
377     if (!l->wbuf_valid_length)
378         l->mainloop->defer_enable(l->defer_event, 1);
379 }
380
381 void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
382     char *t;
383     va_list ap;
384     
385     assert(l);
386     assert(l->ref >= 1);
387
388     va_start(ap, format);
389     t = pa_vsprintf_malloc(format, ap);
390     va_end(ap);
391
392     pa_ioline_puts(l, t);
393     pa_xfree(t);
394 }