source: trunk/third/gnome-vfs2/libgnomevfs/gnome-vfs-socket-buffer.c @ 21416

Revision 21416, 13.7 KB checked in by ghudson, 19 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21415, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* gnome-vfs-socket-buffer.c
3 *
4 * Copyright (C) 2001 Seth Nickell
5 * Copyright (C) 2001 Maciej Stachowiak
6 *
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * The Gnome Library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 */
23/*
24 * Authors: Seth Nickell <snickell@stanford.edu>
25 *          Maciej Stachowiak <mjs@noisehavoc.org>
26 *          (reverse-engineered from code by Ian McKellar <yakk@yakk.net>)
27 */
28
29#include <config.h>
30#include "gnome-vfs-socket-buffer.h"
31
32#include <string.h>
33#include <glib/gmem.h>
34#include <glib/gmessages.h>
35
36
37#define BUFFER_SIZE 4096
38
39struct Buffer {
40        gchar data[BUFFER_SIZE];
41        guint offset;
42        guint byte_count;
43        GnomeVFSResult last_error;
44};
45typedef struct Buffer Buffer;
46
47
48struct  GnomeVFSSocketBuffer {
49        GnomeVFSSocket *socket;
50        Buffer input_buffer;
51        Buffer output_buffer;
52};
53
54
55static void
56buffer_init (Buffer *buffer)
57{
58        buffer->byte_count = 0;
59        buffer->offset = 0;
60        buffer->last_error = GNOME_VFS_OK;
61}
62
63/**
64 * gnome_vfs_socket_buffer_new:
65 * @socket: socket to be buffered
66 *
67 * Create a socket buffer around @socket. A buffered
68 * socket allows data to be poked at without reading it
69 * as it will be buffered. A future read will retrieve
70 * the data again.
71 *
72 * Return value: a newly allocated GnomeVFSSocketBuffer
73 **/
74GnomeVFSSocketBuffer* 
75gnome_vfs_socket_buffer_new (GnomeVFSSocket *socket)
76{
77        GnomeVFSSocketBuffer *socket_buffer;
78
79        g_return_val_if_fail (socket != NULL, NULL);
80
81        socket_buffer = g_new (GnomeVFSSocketBuffer, 1);
82        socket_buffer->socket = socket;
83
84        buffer_init (&socket_buffer->input_buffer);
85        buffer_init (&socket_buffer->output_buffer);
86
87        return socket_buffer;
88}
89
90/**
91 * gnome_vfs_socket_buffer_destroy:
92 * @socket_buffer: buffered socket to destroy.
93 * @close_socket: if %TRUE the socket being buffered will be closed too.
94 * @cancellation: handle allowing cancellation of the operation.
95 *
96 * Free the socket buffer.
97 *
98 * Return value: GnomeVFSResult indicating the success of the operation
99 **/
100GnomeVFSResult   
101gnome_vfs_socket_buffer_destroy  (GnomeVFSSocketBuffer *socket_buffer,
102                                  gboolean close_socket,
103                                  GnomeVFSCancellation *cancellation)
104{
105        gnome_vfs_socket_buffer_flush (socket_buffer, cancellation);
106
107        if (close_socket) {
108                gnome_vfs_socket_close (socket_buffer->socket, cancellation);
109        }
110        g_free (socket_buffer);
111        return GNOME_VFS_OK;
112}
113
114
115
116
117static gboolean
118refill_input_buffer (GnomeVFSSocketBuffer *socket_buffer,
119                     GnomeVFSCancellation *cancellation)
120{
121        Buffer *input_buffer;
122        GnomeVFSResult result;
123        GnomeVFSFileSize bytes_read;
124        char *data_pos;
125
126        input_buffer = &socket_buffer->input_buffer;
127
128        if (input_buffer->last_error != GNOME_VFS_OK) {
129                return FALSE;
130        }
131
132        data_pos = &(input_buffer->data[input_buffer->offset]);
133
134        /* If there is data left in the buffer move it to the front */
135        if (input_buffer->offset > 0) {
136                memmove (input_buffer->data, data_pos, input_buffer->byte_count);
137                data_pos = input_buffer->data;
138        }
139       
140        result = gnome_vfs_socket_read (socket_buffer->socket,
141                                        data_pos,
142                                        BUFFER_SIZE - input_buffer->byte_count,
143                                        &bytes_read,
144                                        cancellation);
145
146        input_buffer->offset = 0;
147       
148        if (result != GNOME_VFS_OK) {
149                input_buffer->last_error = result;
150                return FALSE;
151        }
152
153        input_buffer->byte_count += bytes_read;
154
155        return TRUE;
156}
157
158/**
159 * gnome_vfs_socket_buffer_read:
160 * @socket_buffer: buffered socket to read data from.
161 * @buffer: allocated buffer of at least @bytes bytes to be read into.
162 * @bytes: number of bytes to read from @socket into @socket_buffer.
163 * @bytes_read: pointer to a GnomeVFSFileSize, will contain
164 * the number of bytes actually read from the socket on return.
165 * @cancellation: handle allowing cancellation of the operation.
166 *
167 * Read @bytes bytes of data from the @socket into @socket_buffer.
168 *
169 * Return value: GnomeVFSResult indicating the success of the operation
170 **/
171GnomeVFSResult   
172gnome_vfs_socket_buffer_read (GnomeVFSSocketBuffer *socket_buffer,
173                              gpointer buffer,
174                              GnomeVFSFileSize bytes,
175                              GnomeVFSFileSize *bytes_read,
176                              GnomeVFSCancellation *cancellation)
177{
178        Buffer *input_buffer;
179        GnomeVFSResult result;
180        GnomeVFSFileSize n;
181       
182        g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
183        g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
184       
185        /* Quote from UNIX 98:
186           "If nbyte is 0, read() will return 0 and have no other results."
187        */
188        if (bytes == 0) {
189                if (bytes_read != NULL);
190                        *bytes_read = 0;
191
192                return GNOME_VFS_OK;
193        }
194               
195        input_buffer = &socket_buffer->input_buffer;
196
197        result = GNOME_VFS_OK;
198
199        if (input_buffer->byte_count == 0) {
200                if (! refill_input_buffer (socket_buffer, cancellation)) {
201                        /* The buffer is empty but we had an error last time we
202                           filled it, so we report the error.  */
203                        result = input_buffer->last_error;
204                        input_buffer->last_error = GNOME_VFS_OK;
205                }
206        }
207
208        n = 0;
209       
210        if (input_buffer->byte_count != 0) {
211                n = MIN (bytes, input_buffer->byte_count);
212                memcpy (buffer, input_buffer->data + input_buffer->offset, n);
213                input_buffer->byte_count -= n;
214                input_buffer->offset += n;
215        }
216       
217        if (bytes_read != NULL) {
218                *bytes_read = n;
219        }
220       
221        return result;
222}
223
224/**
225 * gnome_vfs_socket_buffer_read_until:
226 * @socket_buffer: buffered socket to read data from.
227 * @buffer: allocated buffer of at least @bytes bytes to be read into.
228 * @bytes: maximum number of bytes to read from @socket into @socket_buffer.
229 * @boundary: the boundary until wich is read.
230 * @boundary_len: the length of the boundary.
231 * @bytes_read: pointer to a GnomeVFSFileSize, will contain
232 * the number of bytes actually read from the socket on return.
233 * @got_boundary: pointer to a gboolean  which will be %TRUE if the boundary
234 * was found or FALSE otherwise.
235 * @cancellation: handle allowing cancellation of the operation.
236 *
237 * Read up to @bytes bytes of data from the @socket into @socket_buffer
238 * until boundary is reached. @got_boundary will be set accordingly.
239 *
240 * Note that if @bytes is smaller than @boundary_len there is no way
241 * to detected the boundary! So if you want to make sure that every boundary
242 * is found (in a loop maybe) asure that @bytes is at least as big as
243 * @boundary_len.
244 *
245 * Return value: GnomeVFSResult indicating the success of the operation
246 *
247 * Since: 2.8
248 **/
249GnomeVFSResult
250gnome_vfs_socket_buffer_read_until (GnomeVFSSocketBuffer *socket_buffer,
251                                    gpointer buffer,
252                                    GnomeVFSFileSize bytes,
253                                    gconstpointer boundary,
254                                    GnomeVFSFileSize boundary_len,
255                                    GnomeVFSFileSize *bytes_read,
256                                    gboolean *got_boundary,
257                                    GnomeVFSCancellation *cancellation)
258{
259        Buffer *input_buffer;
260        GnomeVFSResult result;
261        GnomeVFSFileSize n, max_scan;
262        char *iter, *start, *delim;
263
264        g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
265        g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
266        g_return_val_if_fail (boundary != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
267        g_return_val_if_fail (got_boundary != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
268        g_return_val_if_fail (boundary_len < BUFFER_SIZE, GNOME_VFS_ERROR_TOO_BIG);
269
270        *got_boundary = FALSE;
271
272        /* Quote from UNIX 98:
273           "If nbyte is 0, read() will return 0 and have no other results."
274        */
275        if (bytes == 0) {
276                if (bytes_read != NULL)
277                        *bytes_read = 0;
278
279                return GNOME_VFS_OK;
280        }
281
282        input_buffer = &socket_buffer->input_buffer;
283        result = GNOME_VFS_OK;
284       
285        /* we are looping here to catch the case where we are close
286         * to eof and haveing less or equal bytes then boundary_len */
287        while (input_buffer->byte_count <= boundary_len) {
288                if (! refill_input_buffer (socket_buffer, cancellation)) {
289                        break;
290                }
291        }
292
293        /* At this point we have either byte_count > boundary_len or
294         * we have and error during refill */
295
296        n = 0;
297        start = input_buffer->data + input_buffer->offset;
298        max_scan = MIN (input_buffer->byte_count, bytes);
299
300        /* if max_scan is greater then boundary_len do a scan (I)
301         * otherwise we had an error during the loop above or bytes is
302         * too small. (II) We handle the case where boundary_len ==
303         * max_scan in (II). */
304
305        if (max_scan > boundary_len) {
306
307                delim = start + max_scan;
308                for (iter = start; iter + boundary_len <= delim; iter++) {
309                        if (!memcmp (iter, boundary, boundary_len)) {
310                                *got_boundary = TRUE;
311                                /* We wanna have the boundary fetched */
312                                iter += boundary_len;
313                                break;
314                        }
315                }
316
317                /* Fetch data data until iter */
318                n = iter - start;
319
320        } else /* (II) */ {
321
322                if (max_scan == boundary_len &&
323                        !memcmp (start, boundary, boundary_len)) {
324                        *got_boundary = TRUE;
325                }
326
327                n = max_scan;
328        }
329
330        if (n > 0) {
331
332                memcpy (buffer, start, n);
333                input_buffer->byte_count -= n;
334                input_buffer->offset += n;
335                /* queque up the fill buffer error if any
336                 * until the buffer is flushed */
337        } else {
338                result = input_buffer->last_error;
339                input_buffer->last_error = GNOME_VFS_OK;
340        }
341
342        if (bytes_read != NULL) {
343                *bytes_read = n;
344        }
345
346        return result;
347}
348
349/**
350 * gnome_vfs_socket_buffer_peekc:
351 * @socket_buffer: the socket buffer to read from.
352 * @character: pointer to a char, will contain a character on return from
353 * a successful "peek".
354 * @cancellation: handle allowing cancellation of the operation.
355 *
356 * Peek at the next character in @socket_buffer without actually reading
357 * the character in. The next read will retrieve @c (as well as any following
358 * data if requested).
359 *
360 * Return value: GnomeVFSResult indicating the success of the operation
361 **/
362GnomeVFSResult
363gnome_vfs_socket_buffer_peekc (GnomeVFSSocketBuffer *socket_buffer,
364                               gchar *character,
365                               GnomeVFSCancellation *cancellation)
366{
367        GnomeVFSResult result;
368        Buffer *input_buffer;
369
370        g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
371        g_return_val_if_fail (character != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
372
373        input_buffer = &socket_buffer->input_buffer;
374        result = GNOME_VFS_OK;
375
376        if (input_buffer->byte_count == 0) {
377                if (!refill_input_buffer (socket_buffer, cancellation)) {
378                        /* The buffer is empty but we had an error last time we
379                           filled it, so we report the error.  */
380                        result = input_buffer->last_error;
381                        input_buffer->last_error = GNOME_VFS_OK;
382                }
383        }
384
385        if (result == GNOME_VFS_OK) {
386                *character = *(input_buffer->data + input_buffer->offset);
387        }
388
389        return result;
390}
391
392
393
394static GnomeVFSResult
395flush (GnomeVFSSocketBuffer *socket_buffer,
396       GnomeVFSCancellation *cancellation)
397{
398        Buffer *output_buffer;
399        GnomeVFSResult result;
400        GnomeVFSFileSize bytes_written;
401
402        output_buffer = &socket_buffer->output_buffer;
403
404        while (output_buffer->byte_count > 0) {
405                result = gnome_vfs_socket_write (socket_buffer->socket,
406                                                 output_buffer->data,
407                                                 output_buffer->byte_count,
408                                                 &bytes_written,
409                                                 cancellation);
410                output_buffer->last_error = result;
411
412                if (result != GNOME_VFS_OK) {
413                        return result;
414                }
415
416                memmove (output_buffer->data,
417                         output_buffer->data + bytes_written,
418                         output_buffer->byte_count - bytes_written);
419                output_buffer->byte_count -= bytes_written;
420        }
421
422        return GNOME_VFS_OK;
423}
424
425/**
426 * gnome_vfs_socket_buffer_write:
427 * @socket_buffer: buffered socket to write data to
428 * @buffer: data to write to the socket
429 * @bytes: number of bytes from @buffer to write to @socket_buffer
430 * @bytes_written: pointer to a GnomeVFSFileSize, will contain
431 * the number of bytes actually written to the socket on return.
432 * @cancellation: handle allowing cancellation of the operation
433 *
434 * Write @bytes bytes of data from @buffer to @socket_buffer.
435 *
436 * Return value: GnomeVFSResult indicating the success of the operation
437 **/
438GnomeVFSResult   
439gnome_vfs_socket_buffer_write (GnomeVFSSocketBuffer *socket_buffer,
440                               gconstpointer buffer,
441                               GnomeVFSFileSize bytes,
442                               GnomeVFSFileSize *bytes_written,
443                               GnomeVFSCancellation *cancellation)
444{
445        Buffer *output_buffer;
446        GnomeVFSFileSize write_count;
447        GnomeVFSResult result;
448        const gchar *p;
449
450        g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
451        g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
452        g_return_val_if_fail (bytes_written != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
453
454        output_buffer = &socket_buffer->output_buffer;
455
456        result = GNOME_VFS_OK;
457
458        p = buffer;
459        write_count = 0;
460        while (write_count < bytes) {
461                if (output_buffer->byte_count < BUFFER_SIZE) {
462                        GnomeVFSFileSize n;
463
464                        n = MIN (BUFFER_SIZE - output_buffer->byte_count,
465                                 bytes - write_count);
466                        memcpy (output_buffer->data + output_buffer->byte_count,
467                                p, n);
468                        p += n;
469                        write_count += n;
470                        output_buffer->byte_count += n;
471                }
472                if (output_buffer->byte_count >= BUFFER_SIZE) {
473                        result = flush (socket_buffer, cancellation);
474                        if (result != GNOME_VFS_OK) {
475                                break;
476                        }
477                }
478        }
479
480        if (bytes_written != NULL) {
481                *bytes_written = write_count;
482        }
483               
484        return result;
485}
486
487/**
488 * gnome_vfs_socket_buffer_flush:
489 * @socket_buffer: buffer to flush
490 * @cancellation: handle allowing cancellation of the operation
491 *
492 * Write all outstanding data to @socket_buffer.
493 *
494 * Return value: GnomeVFSResult indicating the success of the operation
495 **/
496GnomeVFSResult   
497gnome_vfs_socket_buffer_flush (GnomeVFSSocketBuffer *socket_buffer,
498                               GnomeVFSCancellation *cancellation)
499{
500        g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
501
502        return flush (socket_buffer, cancellation);
503}
504
505
506
Note: See TracBrowser for help on using the repository browser.