1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-socket-buffer.c
4 * Copyright (C) 2001 Seth Nickell
5 * Copyright (C) 2001 Maciej Stachowiak
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.
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.
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.
24 * Authors: Seth Nickell <snickell@stanford.edu>
25 * Maciej Stachowiak <mjs@noisehavoc.org>
26 * (reverse-engineered from code by Ian McKellar <yakk@yakk.net>)
30 #include "gnome-vfs-socket-buffer.h"
33 #include <glib/gmem.h>
34 #include <glib/gmessages.h>
37 #define BUFFER_SIZE 4096
40 gchar data[BUFFER_SIZE];
43 GnomeVFSResult last_error;
45 typedef struct Buffer Buffer;
48 struct GnomeVFSSocketBuffer {
49 GnomeVFSSocket *socket;
56 buffer_init (Buffer *buffer)
58 buffer->byte_count = 0;
60 buffer->last_error = GNOME_VFS_OK;
64 * gnome_vfs_socket_buffer_new:
65 * @socket: socket to be buffered
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
72 * Return value: a newly allocated GnomeVFSSocketBuffer
75 gnome_vfs_socket_buffer_new (GnomeVFSSocket *socket)
77 GnomeVFSSocketBuffer *socket_buffer;
79 g_return_val_if_fail (socket != NULL, NULL);
81 socket_buffer = g_new (GnomeVFSSocketBuffer, 1);
82 socket_buffer->socket = socket;
84 buffer_init (&socket_buffer->input_buffer);
85 buffer_init (&socket_buffer->output_buffer);
91 * gnome_vfs_socket_buffer_destroy:
92 * @socket_buffer: buffered socket to destray
93 * @close_socket: if %TRUE the socket being buffered will be closed too
95 * Free the socket buffer.
97 * Return value: GnomeVFSResult indicating the success of the operation
100 gnome_vfs_socket_buffer_destroy (GnomeVFSSocketBuffer *socket_buffer,
101 gboolean close_socket)
103 gnome_vfs_socket_buffer_flush (socket_buffer);
106 gnome_vfs_socket_close (socket_buffer->socket);
108 g_free (socket_buffer);
116 refill_input_buffer (GnomeVFSSocketBuffer *socket_buffer)
118 Buffer *input_buffer;
119 GnomeVFSResult result;
120 GnomeVFSFileSize bytes_read;
122 input_buffer = &socket_buffer->input_buffer;
124 if (input_buffer->last_error != GNOME_VFS_OK
125 || input_buffer->byte_count > 0) {
129 input_buffer->offset = 0;
131 result = gnome_vfs_socket_read (socket_buffer->socket,
136 if (bytes_read == 0) {
137 input_buffer->last_error = GNOME_VFS_ERROR_EOF;
141 input_buffer->byte_count = bytes_read;
147 * gnome_vfs_socket_buffer_read:
148 * @socket_buffer: buffered socket to read data from
149 * @buffer: allocated buffer of at least @bytes bytes to be read into
150 * @bytes: number of bytes to read from @socket into @socket_buffer
151 * @bytes_read: pointer to a GnomeVFSFileSize, will contain
152 * the number of bytes actually read from the socket on return.
154 * Read @bytes bytes of data from the @socket into @socket_buffer.
156 * Return value: GnomeVFSResult indicating the success of the operation
159 gnome_vfs_socket_buffer_read (GnomeVFSSocketBuffer *socket_buffer,
161 GnomeVFSFileSize bytes,
162 GnomeVFSFileSize *bytes_read)
164 Buffer *input_buffer;
165 GnomeVFSResult result;
168 g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
169 g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
171 /* Quote from UNIX 98:
172 "If nbyte is 0, read() will return 0 and have no other results."
179 input_buffer = &socket_buffer->input_buffer;
181 result = GNOME_VFS_OK;
183 if (input_buffer->byte_count == 0) {
184 if (! refill_input_buffer (socket_buffer)) {
185 /* The buffer is empty but we had an error last time we
186 filled it, so we report the error. */
187 result = input_buffer->last_error;
188 input_buffer->last_error = GNOME_VFS_OK;
192 if (input_buffer->byte_count != 0) {
193 n = MIN (bytes, input_buffer->byte_count);
194 memcpy (buffer, input_buffer->data + input_buffer->offset, n);
195 input_buffer->byte_count -= n;
196 input_buffer->offset += n;
197 if (bytes_read != NULL) {
201 if (bytes_read != NULL) {
206 if (result == GNOME_VFS_ERROR_EOF) {
207 result = GNOME_VFS_OK;
214 * gnome_vfs_socket_buffer_peekc:
215 * @socket_buffer: the socket buffer to read from
216 * @character: pointer to a char, will contain a character on return from
217 * a successful "peek"
219 * Peek at the next character in @socket_buffer without actually reading
220 * the character in. The next read will retrieve @c (as well as any following
221 * data if requested).
223 * Return value: GnomeVFSResult indicating the success of the operation
226 gnome_vfs_socket_buffer_peekc (GnomeVFSSocketBuffer *socket_buffer,
229 GnomeVFSResult result;
230 Buffer *input_buffer;
232 g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
233 g_return_val_if_fail (character != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
235 input_buffer = &socket_buffer->input_buffer;
236 result = GNOME_VFS_OK;
238 if (input_buffer->byte_count == 0) {
239 if (!refill_input_buffer (socket_buffer)) {
240 /* The buffer is empty but we had an error last time we
241 filled it, so we report the error. */
242 result = input_buffer->last_error;
243 input_buffer->last_error = GNOME_VFS_OK;
247 if (result == GNOME_VFS_OK) {
248 *character = *(input_buffer->data + input_buffer->offset);
256 static GnomeVFSResult
257 flush (GnomeVFSSocketBuffer *socket_buffer)
259 Buffer *output_buffer;
260 GnomeVFSResult result;
261 GnomeVFSFileSize bytes_written;
263 output_buffer = &socket_buffer->output_buffer;
265 while (output_buffer->byte_count > 0) {
266 result = gnome_vfs_socket_write (socket_buffer->socket,
268 output_buffer->byte_count,
270 output_buffer->last_error = result;
271 output_buffer->byte_count -= bytes_written;
278 * gnome_vfs_socket_buffer_write:
279 * @socket_buffer: buffered socket to write data to
280 * @buffer: data to write to the socket
281 * @bytes: number of bytes from @buffer to write to @socket_buffer
282 * @bytes_written: pointer to a GnomeVFSFileSize, will contain
283 * the number of bytes actually written to the socket on return.
285 * Write @bytes bytes of data from @buffer to @socket_buffer.
287 * Return value: GnomeVFSResult indicating the success of the operation
290 gnome_vfs_socket_buffer_write (GnomeVFSSocketBuffer *socket_buffer,
291 gconstpointer buffer,
292 GnomeVFSFileSize bytes,
293 GnomeVFSFileSize *bytes_written)
295 Buffer *output_buffer;
296 GnomeVFSFileSize write_count;
297 GnomeVFSResult result;
300 g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
301 g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
302 g_return_val_if_fail (bytes_written != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
304 output_buffer = &socket_buffer->output_buffer;
306 result = GNOME_VFS_OK;
310 while (write_count < bytes) {
311 if (output_buffer->byte_count < BUFFER_SIZE) {
314 n = MIN (BUFFER_SIZE - output_buffer->byte_count,
316 memcpy (output_buffer->data + output_buffer->byte_count,
320 output_buffer->byte_count += n;
322 result = flush (socket_buffer);
323 if (result != GNOME_VFS_OK) {
329 if (bytes_written != NULL) {
330 *bytes_written = write_count;
337 * gnome_vfs_socket_buffer_flush:
338 * @socket_buffer: buffer to flush
340 * Write all outstanding data to @socket_buffer.
342 * Return value: GnomeVFSResult indicating the success of the operation
345 gnome_vfs_socket_buffer_flush (GnomeVFSSocketBuffer *socket_buffer)
347 g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
349 return flush (socket_buffer);