mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-24 14:36:13 +01:00
New functions for efficient access to buffer and simple single byte reads.
2007-11-27 Juerg Billeter <j@bitron.ch> * gbufferedinputstream.c: (g_buffered_input_stream_peek_buffer), (g_buffered_input_stream_read_byte): * gbufferedinputstream.h: New functions for efficient access to buffer and simple single byte reads. * gdatainputstream.c: (scan_for_newline), (scan_for_chars), (g_data_input_stream_read_until): * gdatainputstream.h: Use peek_buffer to avoid memcpy in scan_for_newline, implement read_until with multiple stop chars. svn path=/trunk/; revision=5952
This commit is contained in:
parent
2490a699be
commit
6d071b4ab6
@ -1,3 +1,17 @@
|
||||
2007-11-27 Jürg Billeter <j@bitron.ch>
|
||||
|
||||
* gbufferedinputstream.c: (g_buffered_input_stream_peek_buffer),
|
||||
(g_buffered_input_stream_read_byte):
|
||||
* gbufferedinputstream.h:
|
||||
New functions for efficient access to buffer and simple single byte
|
||||
reads.
|
||||
|
||||
* gdatainputstream.c: (scan_for_newline), (scan_for_chars),
|
||||
(g_data_input_stream_read_until):
|
||||
* gdatainputstream.h:
|
||||
Use peek_buffer to avoid memcpy in scan_for_newline, implement
|
||||
read_until with multiple stop chars.
|
||||
|
||||
2007-11-27 Alexander Larsson <alexl@redhat.com>
|
||||
|
||||
* Makefile.am:
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
* Copyright (C) 2007 Jürg Billeter
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -577,6 +578,34 @@ g_buffered_input_stream_peek (GBufferedInputStream *stream,
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_input_stream_peek_buffer:
|
||||
* @stream: a #GBufferedInputStream.
|
||||
* @count: a #gsize to get the number of bytes available in the buffer.
|
||||
*
|
||||
* Returns the buffer with the currently available bytes. The returned
|
||||
* buffer must not be modified and will become invalid when reading from
|
||||
* the stream or filling the buffer.
|
||||
*
|
||||
* Returns: read-only buffer
|
||||
**/
|
||||
const void*
|
||||
g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream,
|
||||
gsize *count)
|
||||
{
|
||||
GBufferedInputStreamPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), NULL);
|
||||
|
||||
priv = stream->priv;
|
||||
|
||||
if (count) {
|
||||
*count = priv->end - priv->pos;
|
||||
}
|
||||
|
||||
return priv->buffer + priv->pos;
|
||||
}
|
||||
|
||||
static void
|
||||
compact_buffer (GBufferedInputStream *stream)
|
||||
{
|
||||
@ -785,6 +814,81 @@ g_buffered_input_stream_read (GInputStream *stream,
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_input_stream_read_byte:
|
||||
* @stream: #GBufferedInputStream.
|
||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
||||
* @error: location to store the error occuring, or %NULL to ignore.
|
||||
*
|
||||
* Tries to read a single byte from the stream or the buffer. Will block
|
||||
* during this read.
|
||||
*
|
||||
* On success, the byte read from the stream is returned. On end of stream
|
||||
* -1 is returned but it's not an exceptional error and @error is not set.
|
||||
*
|
||||
* If @cancellable is not %NULL, then the operation can be cancelled by
|
||||
* triggering the cancellable object from another thread. If the operation
|
||||
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
|
||||
* operation was partially finished when the operation was cancelled the
|
||||
* partial result will be returned, without an error.
|
||||
*
|
||||
* On error -1 is returned and @error is set accordingly.
|
||||
*
|
||||
* Returns: the byte read from the @stream, or -1 on end of stream or error.
|
||||
**/
|
||||
int
|
||||
g_buffered_input_stream_read_byte (GBufferedInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedInputStreamPrivate *priv;
|
||||
GInputStream *input_stream;
|
||||
gsize available;
|
||||
gssize nread;
|
||||
|
||||
g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
|
||||
|
||||
priv = stream->priv;
|
||||
input_stream = G_INPUT_STREAM (stream);
|
||||
|
||||
if (g_input_stream_is_closed (input_stream))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
|
||||
_("Stream is already closed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_input_stream_has_pending (input_stream))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
|
||||
_("Stream has outstanding operation"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
available = priv->end - priv->pos;
|
||||
|
||||
if (available < 1)
|
||||
return priv->buffer[priv->pos++];
|
||||
|
||||
/* Byte not available, request refill for more */
|
||||
|
||||
if (cancellable)
|
||||
g_push_current_cancellable (cancellable);
|
||||
|
||||
priv->pos = 0;
|
||||
priv->end = 0;
|
||||
|
||||
nread = g_buffered_input_stream_fill (stream, priv->len, cancellable, error);
|
||||
|
||||
if (cancellable)
|
||||
g_pop_current_cancellable (cancellable);
|
||||
|
||||
if (nread <= 0)
|
||||
return -1; /* error or end of stream */
|
||||
|
||||
return priv->buffer[priv->pos++];
|
||||
}
|
||||
|
||||
/* ************************** */
|
||||
/* Async stuff implementation */
|
||||
/* ************************** */
|
||||
|
@ -89,6 +89,8 @@ gsize g_buffered_input_stream_peek (GBufferedInputStream *st
|
||||
void *buffer,
|
||||
gsize offset,
|
||||
gsize count);
|
||||
const void* g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream,
|
||||
gsize *count);
|
||||
|
||||
gssize g_buffered_input_stream_fill (GBufferedInputStream *stream,
|
||||
gssize count,
|
||||
@ -104,6 +106,10 @@ gssize g_buffered_input_stream_fill_finish (GBufferedInputStream *st
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
int g_buffered_input_stream_read_byte (GBufferedInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
* Copyright (C) 2007 Jürg Billeter
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -523,7 +524,7 @@ scan_for_newline (GDataInputStream *stream,
|
||||
{
|
||||
GBufferedInputStream *bstream;
|
||||
GDataInputStreamPrivate *priv;
|
||||
char buffer[100];
|
||||
const char *buffer;
|
||||
gsize start, end, peeked;
|
||||
int i;
|
||||
gssize found_pos;
|
||||
@ -534,84 +535,80 @@ scan_for_newline (GDataInputStream *stream,
|
||||
priv = stream->priv;
|
||||
|
||||
bstream = G_BUFFERED_INPUT_STREAM (stream);
|
||||
|
||||
available = g_buffered_input_stream_get_available (bstream);
|
||||
|
||||
checked = *checked_out;
|
||||
last_saw_cr = *last_saw_cr_out;
|
||||
found_pos = -1;
|
||||
newline_len = 0;
|
||||
|
||||
while (checked < available)
|
||||
{
|
||||
start = checked;
|
||||
end = MIN (start + sizeof(buffer), available);
|
||||
peeked = g_buffered_input_stream_peek (bstream, buffer, start, end - start);
|
||||
end = start + peeked;
|
||||
start = checked;
|
||||
buffer = g_buffered_input_stream_peek_buffer (bstream, &available) + start;
|
||||
end = available;
|
||||
peeked = end - start;
|
||||
|
||||
for (i = 0; i < peeked; i++)
|
||||
for (i = 0; checked < available && i < peeked; i++)
|
||||
{
|
||||
switch (priv->newline_type)
|
||||
{
|
||||
switch (priv->newline_type)
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_LF:
|
||||
if (buffer[i] == 10)
|
||||
{
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_LF:
|
||||
if (buffer[i] == 10)
|
||||
{
|
||||
found_pos = start + i;
|
||||
newline_len = 1;
|
||||
}
|
||||
break;
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_CR:
|
||||
if (buffer[i] == 13)
|
||||
{
|
||||
found_pos = start + i;
|
||||
newline_len = 1;
|
||||
}
|
||||
break;
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_CR_LF:
|
||||
if (last_saw_cr && buffer[i] == 10)
|
||||
found_pos = start + i;
|
||||
newline_len = 1;
|
||||
}
|
||||
break;
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_CR:
|
||||
if (buffer[i] == 13)
|
||||
{
|
||||
found_pos = start + i;
|
||||
newline_len = 1;
|
||||
}
|
||||
break;
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_CR_LF:
|
||||
if (last_saw_cr && buffer[i] == 10)
|
||||
{
|
||||
found_pos = start + i - 1;
|
||||
newline_len = 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_ANY:
|
||||
if (buffer[i] == 10) /* LF */
|
||||
{
|
||||
if (last_saw_cr)
|
||||
{
|
||||
/* CR LF */
|
||||
found_pos = start + i - 1;
|
||||
newline_len = 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case G_DATA_STREAM_NEWLINE_TYPE_ANY:
|
||||
if (buffer[i] == 10) /* LF */
|
||||
else
|
||||
{
|
||||
if (last_saw_cr)
|
||||
{
|
||||
/* CR LF */
|
||||
found_pos = start + i - 1;
|
||||
newline_len = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* LF */
|
||||
found_pos = start + i;
|
||||
newline_len = 1;
|
||||
}
|
||||
}
|
||||
else if (last_saw_cr)
|
||||
{
|
||||
/* Last was cr, this is not LF, end is CR */
|
||||
found_pos = start + i - 1;
|
||||
/* LF */
|
||||
found_pos = start + i;
|
||||
newline_len = 1;
|
||||
}
|
||||
/* Don't check for CR here, instead look at last_saw_cr on next byte */
|
||||
break;
|
||||
}
|
||||
|
||||
last_saw_cr = (buffer[i] == 13);
|
||||
|
||||
if (found_pos != -1)
|
||||
else if (last_saw_cr)
|
||||
{
|
||||
*newline_len_out = newline_len;
|
||||
return found_pos;
|
||||
/* Last was cr, this is not LF, end is CR */
|
||||
found_pos = start + i - 1;
|
||||
newline_len = 1;
|
||||
}
|
||||
/* Don't check for CR here, instead look at last_saw_cr on next byte */
|
||||
break;
|
||||
}
|
||||
|
||||
last_saw_cr = (buffer[i] == 13);
|
||||
|
||||
if (found_pos != -1)
|
||||
{
|
||||
*newline_len_out = newline_len;
|
||||
return found_pos;
|
||||
}
|
||||
checked = end;
|
||||
}
|
||||
|
||||
checked = end;
|
||||
|
||||
*checked_out = checked;
|
||||
*last_saw_cr_out = last_saw_cr;
|
||||
return -1;
|
||||
@ -694,25 +691,118 @@ g_data_input_stream_read_line (GDataInputStream *stream,
|
||||
}
|
||||
|
||||
|
||||
static gssize
|
||||
scan_for_chars (GDataInputStream *stream,
|
||||
gsize *checked_out,
|
||||
const char *stop_chars)
|
||||
{
|
||||
GBufferedInputStream *bstream;
|
||||
GDataInputStreamPrivate *priv;
|
||||
const char *buffer;
|
||||
gsize start, end, peeked;
|
||||
int i;
|
||||
gssize found_pos;
|
||||
gsize available, checked;
|
||||
const char *stop_char;
|
||||
|
||||
priv = stream->priv;
|
||||
|
||||
bstream = G_BUFFERED_INPUT_STREAM (stream);
|
||||
|
||||
checked = *checked_out;
|
||||
found_pos = -1;
|
||||
|
||||
start = checked;
|
||||
buffer = g_buffered_input_stream_peek_buffer (bstream, &available) + start;
|
||||
end = available;
|
||||
peeked = end - start;
|
||||
|
||||
for (i = 0; checked < available && i < peeked; i++)
|
||||
{
|
||||
for (stop_char = stop_chars; *stop_char != '\0'; stop_char++)
|
||||
{
|
||||
if (buffer[i] == *stop_char)
|
||||
return (start + i);
|
||||
}
|
||||
}
|
||||
|
||||
checked = end;
|
||||
|
||||
*checked_out = checked;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_input_stream_read_until:
|
||||
* @stream: a given #GDataInputStream.
|
||||
* @stop_char: character to terminate the read.
|
||||
* @stop_chars: characters to terminate the read.
|
||||
* @length: a #gsize to get the length of the data read in.
|
||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
||||
* @error: #GError for error reporting.
|
||||
*
|
||||
* NOTE: not supported for #GDataInputStream.
|
||||
* Returns %NULL.
|
||||
* Returns a string with the data that was read before encountering any of
|
||||
* the stop characters. Set @length to a #gsize to get the length of the
|
||||
* read line. This function will return %NULL on an error.
|
||||
**/
|
||||
char *
|
||||
g_data_input_stream_read_until (GDataInputStream *stream,
|
||||
gchar stop_char,
|
||||
const gchar *stop_chars,
|
||||
gsize *length,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
/* TODO: should be implemented */
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
GBufferedInputStream *bstream;
|
||||
gsize checked;
|
||||
gssize found_pos;
|
||||
gssize res;
|
||||
int stop_char_len;
|
||||
char *data_until;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL);
|
||||
|
||||
bstream = G_BUFFERED_INPUT_STREAM (stream);
|
||||
|
||||
stop_char_len = 1;
|
||||
checked = 0;
|
||||
|
||||
while ((found_pos = scan_for_chars (stream, &checked, stop_chars)) == -1)
|
||||
{
|
||||
if (g_buffered_input_stream_get_available (bstream) ==
|
||||
g_buffered_input_stream_get_buffer_size (bstream))
|
||||
g_buffered_input_stream_set_buffer_size (bstream,
|
||||
2 * g_buffered_input_stream_get_buffer_size (bstream));
|
||||
|
||||
res = g_buffered_input_stream_fill (bstream, -1, cancellable, error);
|
||||
if (res < 0)
|
||||
return NULL;
|
||||
if (res == 0)
|
||||
{
|
||||
/* End of stream */
|
||||
if (g_buffered_input_stream_get_available (bstream) == 0)
|
||||
{
|
||||
if (length)
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
found_pos = checked;
|
||||
stop_char_len = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_until = g_malloc (found_pos + stop_char_len + 1);
|
||||
|
||||
res = g_input_stream_read (G_INPUT_STREAM (stream),
|
||||
data_until,
|
||||
found_pos + stop_char_len,
|
||||
NULL, NULL);
|
||||
if (length)
|
||||
*length = (gsize)found_pos;
|
||||
g_assert (res == found_pos + stop_char_len);
|
||||
data_until[found_pos] = 0;
|
||||
|
||||
return data_until;
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ char * g_data_input_stream_read_line (GDataInputStream
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char * g_data_input_stream_read_until (GDataInputStream *stream,
|
||||
gchar stop_char,
|
||||
const gchar *stop_chars,
|
||||
gsize *length,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
Loading…
Reference in New Issue
Block a user