gbufferedinputstream.c 37.6 KB
Newer Older
1
/* GIO - GLib Input, Output and Streaming Library
Matthias Clasen's avatar
Matthias Clasen committed
2
 *
3
 * Copyright (C) 2006-2007 Red Hat, Inc.
4
 * Copyright (C) 2007 Jürg Billeter
5 6 7 8
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10 11 12 13 14 15 16
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
Matthias Clasen's avatar
Matthias Clasen committed
19
 * Author: Christian Kellner <gicmo@gnome.org>
20 21
 */

22
#include "config.h"
23 24
#include "gbufferedinputstream.h"
#include "ginputstream.h"
25 26
#include "gcancellable.h"
#include "gasyncresult.h"
27
#include "gtask.h"
28
#include "gseekable.h"
29
#include "gioerror.h"
30 31 32
#include <string.h>
#include "glibintl.h"

33

34 35 36
/**
 * SECTION:gbufferedinputstream
 * @short_description: Buffered Input Stream
Matthias Clasen's avatar
Matthias Clasen committed
37
 * @include: gio/gio.h
38
 * @see_also: #GFilterInputStream, #GInputStream
Matthias Clasen's avatar
Matthias Clasen committed
39 40 41 42
 *
 * Buffered input stream implements #GFilterInputStream and provides
 * for buffered reads.
 *
43
 * By default, #GBufferedInputStream's buffer size is set at 4 kilobytes.
Matthias Clasen's avatar
Matthias Clasen committed
44 45 46
 *
 * To create a buffered input stream, use g_buffered_input_stream_new(),
 * or g_buffered_input_stream_new_sized() to specify the buffer's size at
47
 * construction.
Matthias Clasen's avatar
Matthias Clasen committed
48 49 50
 *
 * To get the size of a buffer within a buffered input stream, use
 * g_buffered_input_stream_get_buffer_size(). To change the size of a
51
 * buffered input stream's buffer, use
Matthias Clasen's avatar
Matthias Clasen committed
52
 * g_buffered_input_stream_set_buffer_size(). Note that the buffer's size
53
 * cannot be reduced below the size of the data within the buffer.
Matthias Clasen's avatar
Matthias Clasen committed
54
 */
55 56


57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#define DEFAULT_BUFFER_SIZE 4096

struct _GBufferedInputStreamPrivate {
  guint8 *buffer;
  gsize   len;
  gsize   pos;
  gsize   end;
  GAsyncReadyCallback outstanding_callback;
};

enum {
  PROP_0,
  PROP_BUFSIZE
};

static void g_buffered_input_stream_set_property  (GObject      *object,
                                                   guint         prop_id,
                                                   const GValue *value,
                                                   GParamSpec   *pspec);

static void g_buffered_input_stream_get_property  (GObject      *object,
                                                   guint         prop_id,
                                                   GValue       *value,
                                                   GParamSpec   *pspec);
static void g_buffered_input_stream_finalize      (GObject *object);


static gssize g_buffered_input_stream_skip             (GInputStream          *stream,
Matthias Clasen's avatar
Matthias Clasen committed
85 86 87
                                                        gsize                  count,
                                                        GCancellable          *cancellable,
                                                        GError               **error);
88
static void   g_buffered_input_stream_skip_async       (GInputStream          *stream,
Matthias Clasen's avatar
Matthias Clasen committed
89 90 91 92 93
                                                        gsize                  count,
                                                        int                    io_priority,
                                                        GCancellable          *cancellable,
                                                        GAsyncReadyCallback    callback,
                                                        gpointer               user_data);
94
static gssize g_buffered_input_stream_skip_finish      (GInputStream          *stream,
Matthias Clasen's avatar
Matthias Clasen committed
95 96
                                                        GAsyncResult          *result,
                                                        GError               **error);
97
static gssize g_buffered_input_stream_read             (GInputStream          *stream,
Matthias Clasen's avatar
Matthias Clasen committed
98 99 100 101
                                                        void                  *buffer,
                                                        gsize                  count,
                                                        GCancellable          *cancellable,
                                                        GError               **error);
102
static gssize g_buffered_input_stream_real_fill        (GBufferedInputStream  *stream,
Matthias Clasen's avatar
Matthias Clasen committed
103 104 105
                                                        gssize                 count,
                                                        GCancellable          *cancellable,
                                                        GError               **error);
106
static void   g_buffered_input_stream_real_fill_async  (GBufferedInputStream  *stream,
Matthias Clasen's avatar
Matthias Clasen committed
107 108 109 110 111
                                                        gssize                 count,
                                                        int                    io_priority,
                                                        GCancellable          *cancellable,
                                                        GAsyncReadyCallback    callback,
                                                        gpointer               user_data);
112
static gssize g_buffered_input_stream_real_fill_finish (GBufferedInputStream  *stream,
Matthias Clasen's avatar
Matthias Clasen committed
113 114
                                                        GAsyncResult          *result,
                                                        GError               **error);
115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
static void     g_buffered_input_stream_seekable_iface_init (GSeekableIface  *iface);
static goffset  g_buffered_input_stream_tell                (GSeekable       *seekable);
static gboolean g_buffered_input_stream_can_seek            (GSeekable       *seekable);
static gboolean g_buffered_input_stream_seek                (GSeekable       *seekable,
							     goffset          offset,
							     GSeekType        type,
							     GCancellable    *cancellable,
							     GError         **error);
static gboolean g_buffered_input_stream_can_truncate        (GSeekable       *seekable);
static gboolean g_buffered_input_stream_truncate            (GSeekable       *seekable,
							     goffset          offset,
							     GCancellable    *cancellable,
							     GError         **error);

static void compact_buffer (GBufferedInputStream *stream);
131

132 133 134
G_DEFINE_TYPE_WITH_CODE (GBufferedInputStream,
			 g_buffered_input_stream,
			 G_TYPE_FILTER_INPUT_STREAM,
135
                         G_ADD_PRIVATE (GBufferedInputStream)
136 137
			 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
						g_buffered_input_stream_seekable_iface_init))
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

static void
g_buffered_input_stream_class_init (GBufferedInputStreamClass *klass)
{
  GObjectClass *object_class;
  GInputStreamClass *istream_class;
  GBufferedInputStreamClass *bstream_class;

  object_class = G_OBJECT_CLASS (klass);
  object_class->get_property = g_buffered_input_stream_get_property;
  object_class->set_property = g_buffered_input_stream_set_property;
  object_class->finalize     = g_buffered_input_stream_finalize;

  istream_class = G_INPUT_STREAM_CLASS (klass);
  istream_class->skip = g_buffered_input_stream_skip;
  istream_class->skip_async  = g_buffered_input_stream_skip_async;
  istream_class->skip_finish = g_buffered_input_stream_skip_finish;
155
  istream_class->read_fn = g_buffered_input_stream_read;
156 157 158 159 160

  bstream_class = G_BUFFERED_INPUT_STREAM_CLASS (klass);
  bstream_class->fill = g_buffered_input_stream_real_fill;
  bstream_class->fill_async = g_buffered_input_stream_real_fill_async;
  bstream_class->fill_finish = g_buffered_input_stream_real_fill_finish;
Matthias Clasen's avatar
Matthias Clasen committed
161

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
  g_object_class_install_property (object_class,
                                   PROP_BUFSIZE,
                                   g_param_spec_uint ("buffer-size",
                                                      P_("Buffer Size"),
                                                      P_("The size of the backend buffer"),
                                                      1,
                                                      G_MAXUINT,
                                                      DEFAULT_BUFFER_SIZE,
                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));


}

/**
 * g_buffered_input_stream_get_buffer_size:
Matthias Clasen's avatar
Matthias Clasen committed
178 179
 * @stream: a #GBufferedInputStream
 *
180
 * Gets the size of the input buffer.
Matthias Clasen's avatar
Matthias Clasen committed
181
 *
182
 * Returns: the current buffer size.
Matthias Clasen's avatar
Matthias Clasen committed
183
 */
184 185 186
gsize
g_buffered_input_stream_get_buffer_size (GBufferedInputStream  *stream)
{
187
  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), 0);
188 189 190 191 192 193

  return stream->priv->len;
}

/**
 * g_buffered_input_stream_set_buffer_size:
Matthias Clasen's avatar
Matthias Clasen committed
194 195
 * @stream: a #GBufferedInputStream
 * @size: a #gsize
196
 *
Matthias Clasen's avatar
Matthias Clasen committed
197 198
 * Sets the size of the internal buffer of @stream to @size, or to the
 * size of the contents of the buffer. The buffer can never be resized
199
 * smaller than its current contents.
Matthias Clasen's avatar
Matthias Clasen committed
200
 */
201
void
Matthias Clasen's avatar
Matthias Clasen committed
202 203
g_buffered_input_stream_set_buffer_size (GBufferedInputStream *stream,
                                         gsize                 size)
204 205 206 207
{
  GBufferedInputStreamPrivate *priv;
  gsize in_buffer;
  guint8 *buffer;
Matthias Clasen's avatar
Matthias Clasen committed
208

209 210 211 212
  g_return_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream));

  priv = stream->priv;

213 214 215
  if (priv->len == size)
    return;

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
  if (priv->buffer)
    {
      in_buffer = priv->end - priv->pos;

      /* Never resize smaller than current buffer contents */
      size = MAX (size, in_buffer);

      buffer = g_malloc (size);
      memcpy (buffer, priv->buffer + priv->pos, in_buffer);
      priv->len = size;
      priv->pos = 0;
      priv->end = in_buffer;
      g_free (priv->buffer);
      priv->buffer = buffer;
    }
  else
    {
      priv->len = size;
      priv->pos = 0;
      priv->end = 0;
      priv->buffer = g_malloc (size);
    }
238 239

  g_object_notify (G_OBJECT (stream), "buffer-size");
240 241 242
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
243 244 245 246
g_buffered_input_stream_set_property (GObject      *object,
                                      guint         prop_id,
                                      const GValue *value,
                                      GParamSpec   *pspec)
247 248 249 250 251
{
  GBufferedInputStream        *bstream;

  bstream = G_BUFFERED_INPUT_STREAM (object);

252
  switch (prop_id)
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
    {
    case PROP_BUFSIZE:
      g_buffered_input_stream_set_buffer_size (bstream, g_value_get_uint (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
g_buffered_input_stream_get_property (GObject    *object,
                                      guint       prop_id,
                                      GValue     *value,
                                      GParamSpec *pspec)
{
  GBufferedInputStreamPrivate *priv;
  GBufferedInputStream        *bstream;

  bstream = G_BUFFERED_INPUT_STREAM (object);
  priv = bstream->priv;

  switch (prop_id)
Matthias Clasen's avatar
Matthias Clasen committed
277
    {
278 279 280
    case PROP_BUFSIZE:
      g_value_set_uint (value, priv->len);
      break;
Matthias Clasen's avatar
Matthias Clasen committed
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
g_buffered_input_stream_finalize (GObject *object)
{
  GBufferedInputStreamPrivate *priv;
  GBufferedInputStream        *stream;

  stream = G_BUFFERED_INPUT_STREAM (object);
  priv = stream->priv;

  g_free (priv->buffer);

299
  G_OBJECT_CLASS (g_buffered_input_stream_parent_class)->finalize (object);
300 301
}

302 303 304 305 306 307 308 309 310 311
static void
g_buffered_input_stream_seekable_iface_init (GSeekableIface *iface)
{
  iface->tell         = g_buffered_input_stream_tell;
  iface->can_seek     = g_buffered_input_stream_can_seek;
  iface->seek         = g_buffered_input_stream_seek;
  iface->can_truncate = g_buffered_input_stream_can_truncate;
  iface->truncate_fn  = g_buffered_input_stream_truncate;
}

312 313 314
static void
g_buffered_input_stream_init (GBufferedInputStream *stream)
{
315
  stream->priv = g_buffered_input_stream_get_instance_private (stream);
316 317 318 319 320
}


/**
 * g_buffered_input_stream_new:
Matthias Clasen's avatar
Matthias Clasen committed
321 322 323
 * @base_stream: a #GInputStream
 *
 * Creates a new #GInputStream from the given @base_stream, with
324 325 326
 * a buffer set to the default size (4 kilobytes).
 *
 * Returns: a #GInputStream for the given @base_stream.
Matthias Clasen's avatar
Matthias Clasen committed
327
 */
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
GInputStream *
g_buffered_input_stream_new (GInputStream *base_stream)
{
  GInputStream *stream;

  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);

  stream = g_object_new (G_TYPE_BUFFERED_INPUT_STREAM,
                         "base-stream", base_stream,
                         NULL);

  return stream;
}

/**
 * g_buffered_input_stream_new_sized:
Matthias Clasen's avatar
Matthias Clasen committed
344 345 346 347
 * @base_stream: a #GInputStream
 * @size: a #gsize
 *
 * Creates a new #GBufferedInputStream from the given @base_stream,
Matthias Clasen's avatar
Matthias Clasen committed
348
 * with a buffer set to @size.
349 350
 *
 * Returns: a #GInputStream.
Matthias Clasen's avatar
Matthias Clasen committed
351
 */
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
GInputStream *
g_buffered_input_stream_new_sized (GInputStream *base_stream,
                                   gsize         size)
{
  GInputStream *stream;

  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);

  stream = g_object_new (G_TYPE_BUFFERED_INPUT_STREAM,
                         "base-stream", base_stream,
                         "buffer-size", (guint)size,
                         NULL);

  return stream;
}

/**
 * g_buffered_input_stream_fill:
Matthias Clasen's avatar
Matthias Clasen committed
370 371
 * @stream: a #GBufferedInputStream
 * @count: the number of bytes that will be read from the stream
372
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
Matthias Clasen's avatar
Matthias Clasen committed
373
 * @error: location to store the error occurring, or %NULL to ignore
374
 *
Matthias Clasen's avatar
Matthias Clasen committed
375
 * Tries to read @count bytes from the stream into the buffer.
376
 * Will block during this read.
Matthias Clasen's avatar
Matthias Clasen committed
377
 *
378 379 380 381 382 383 384 385
 * If @count is zero, returns zero and does nothing. A value of @count
 * larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
 *
 * On success, the number of bytes read into the buffer is returned.
 * It is not an error if this is not the same as the requested size, as it
 * can happen e.g. near the end of a file. Zero is returned on end of file
 * (or if @count is zero),  but never otherwise.
 *
386 387 388
 * If @count is -1 then the attempted read size is equal to the number of
 * bytes that are required to fill the buffer.
 *
389 390 391 392 393 394 395
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
396 397
 *
 * For the asynchronous, non-blocking, version of this function, see
398 399
 * g_buffered_input_stream_fill_async().
 *
Matthias Clasen's avatar
Matthias Clasen committed
400
 * Returns: the number of bytes read into @stream's buffer, up to @count,
Matthias Clasen's avatar
Matthias Clasen committed
401
 *     or -1 on error.
Matthias Clasen's avatar
Matthias Clasen committed
402
 */
403 404
gssize
g_buffered_input_stream_fill (GBufferedInputStream  *stream,
Matthias Clasen's avatar
Matthias Clasen committed
405 406 407
                              gssize                 count,
                              GCancellable          *cancellable,
                              GError               **error)
408 409 410 411 412 413
{
  GBufferedInputStreamClass *class;
  GInputStream *input_stream;
  gssize res;

  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
Matthias Clasen's avatar
Matthias Clasen committed
414

415
  input_stream = G_INPUT_STREAM (stream);
Matthias Clasen's avatar
Matthias Clasen committed
416

417 418 419 420 421 422 423
  if (count < -1)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                   _("Too large count value passed to %s"), G_STRFUNC);
      return -1;
    }

424 425
  if (!g_input_stream_set_pending (input_stream, error))
    return -1;
426 427

  if (cancellable)
428
    g_cancellable_push_current (cancellable);
Matthias Clasen's avatar
Matthias Clasen committed
429

430 431 432 433
  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
  res = class->fill (stream, count, cancellable, error);

  if (cancellable)
434
    g_cancellable_pop_current (cancellable);
Matthias Clasen's avatar
Matthias Clasen committed
435

436
  g_input_stream_clear_pending (input_stream);
Matthias Clasen's avatar
Matthias Clasen committed
437

438 439 440 441
  return res;
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
442 443 444
async_fill_callback_wrapper (GObject      *source_object,
                             GAsyncResult *res,
                             gpointer      user_data)
445 446 447
{
  GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (source_object);

448
  g_input_stream_clear_pending (G_INPUT_STREAM (stream));
449 450 451 452 453 454
  (*stream->priv->outstanding_callback) (source_object, res, user_data);
  g_object_unref (stream);
}

/**
 * g_buffered_input_stream_fill_async:
Matthias Clasen's avatar
Matthias Clasen committed
455 456
 * @stream: a #GBufferedInputStream
 * @count: the number of bytes that will be read from the stream
457
 * @io_priority: the [I/O priority][io-priority] of the request
458
 * @cancellable: (nullable): optional #GCancellable object
459 460
 * @callback: (scope async): a #GAsyncReadyCallback
 * @user_data: (closure): a #gpointer
461 462 463 464
 *
 * Reads data into @stream's buffer asynchronously, up to @count size.
 * @io_priority can be used to prioritize reads. For the synchronous
 * version of this function, see g_buffered_input_stream_fill().
465 466 467
 *
 * If @count is -1 then the attempted read size is equal to the number
 * of bytes that are required to fill the buffer.
Matthias Clasen's avatar
Matthias Clasen committed
468
 */
469
void
Matthias Clasen's avatar
Matthias Clasen committed
470 471 472 473 474 475
g_buffered_input_stream_fill_async (GBufferedInputStream *stream,
                                    gssize                count,
                                    int                   io_priority,
                                    GCancellable         *cancellable,
                                    GAsyncReadyCallback   callback,
                                    gpointer              user_data)
476 477
{
  GBufferedInputStreamClass *class;
478
  GError *error = NULL;
479 480 481 482 483

  g_return_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream));

  if (count == 0)
    {
484 485 486 487 488 489
      GTask *task;

      task = g_task_new (stream, cancellable, callback, user_data);
      g_task_set_source_tag (task, g_buffered_input_stream_fill_async);
      g_task_return_int (task, 0);
      g_object_unref (task);
490 491
      return;
    }
Matthias Clasen's avatar
Matthias Clasen committed
492

493
  if (count < -1)
494
    {
495 496 497 498 499
      g_task_report_new_error (stream, callback, user_data,
                               g_buffered_input_stream_fill_async,
                               G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
			       _("Too large count value passed to %s"),
			       G_STRFUNC);
500 501
      return;
    }
Matthias Clasen's avatar
Matthias Clasen committed
502

503
  if (!g_input_stream_set_pending (G_INPUT_STREAM (stream), &error))
504
    {
505 506 507
      g_task_report_error (stream, callback, user_data,
                           g_buffered_input_stream_fill_async,
                           error);
508 509
      return;
    }
Matthias Clasen's avatar
Matthias Clasen committed
510

511
  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
Matthias Clasen's avatar
Matthias Clasen committed
512

513 514 515
  stream->priv->outstanding_callback = callback;
  g_object_ref (stream);
  class->fill_async (stream, count, io_priority, cancellable,
Matthias Clasen's avatar
Matthias Clasen committed
516
                     async_fill_callback_wrapper, user_data);
517 518 519
}

/**
520
 * g_buffered_input_stream_fill_finish:
Matthias Clasen's avatar
Matthias Clasen committed
521 522 523
 * @stream: a #GBufferedInputStream
 * @result: a #GAsyncResult
 * @error: a #GError
524 525
 *
 * Finishes an asynchronous read.
Matthias Clasen's avatar
Matthias Clasen committed
526
 *
527
 * Returns: a #gssize of the read stream, or `-1` on an error.
Matthias Clasen's avatar
Matthias Clasen committed
528
 */
529 530
gssize
g_buffered_input_stream_fill_finish (GBufferedInputStream  *stream,
Matthias Clasen's avatar
Matthias Clasen committed
531 532
                                     GAsyncResult          *result,
                                     GError               **error)
533 534 535 536 537 538
{
  GBufferedInputStreamClass *class;

  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);

539 540
  if (g_async_result_legacy_propagate_error (result, error))
    return -1;
541
  else if (g_async_result_is_tagged (result, g_buffered_input_stream_fill_async))
542
    return g_task_propagate_int (G_TASK (result), error);
543 544 545 546 547 548 549

  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
  return class->fill_finish (stream, result, error);
}

/**
 * g_buffered_input_stream_get_available:
Matthias Clasen's avatar
Matthias Clasen committed
550 551
 * @stream: #GBufferedInputStream
 *
552
 * Gets the size of the available data within the stream.
Matthias Clasen's avatar
Matthias Clasen committed
553 554 555
 *
 * Returns: size of the available stream.
 */
556
gsize
Matthias Clasen's avatar
Matthias Clasen committed
557
g_buffered_input_stream_get_available (GBufferedInputStream *stream)
558 559 560 561 562 563 564 565
{
  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);

  return stream->priv->end - stream->priv->pos;
}

/**
 * g_buffered_input_stream_peek:
Matthias Clasen's avatar
Matthias Clasen committed
566
 * @stream: a #GBufferedInputStream
567 568
 * @buffer: (array length=count) (element-type guint8): a pointer to
 *   an allocated chunk of memory
Matthias Clasen's avatar
Matthias Clasen committed
569 570 571 572
 * @offset: a #gsize
 * @count: a #gsize
 *
 * Peeks in the buffer, copying data of size @count into @buffer,
Matthias Clasen's avatar
Matthias Clasen committed
573
 * offset @offset bytes.
Matthias Clasen's avatar
Matthias Clasen committed
574 575 576
 *
 * Returns: a #gsize of the number of bytes peeked, or -1 on error.
 */
577
gsize
Matthias Clasen's avatar
Matthias Clasen committed
578 579 580 581
g_buffered_input_stream_peek (GBufferedInputStream *stream,
                              void                 *buffer,
                              gsize                 offset,
                              gsize                 count)
582 583 584
{
  gsize available;
  gsize end;
Matthias Clasen's avatar
Matthias Clasen committed
585

586 587 588 589 590 591 592
  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
  g_return_val_if_fail (buffer != NULL, -1);

  available = g_buffered_input_stream_get_available (stream);

  if (offset > available)
    return 0;
Matthias Clasen's avatar
Matthias Clasen committed
593

594 595
  end = MIN (offset + count, available);
  count = end - offset;
Matthias Clasen's avatar
Matthias Clasen committed
596

597 598 599 600
  memcpy (buffer, stream->priv->buffer + stream->priv->pos + offset, count);
  return count;
}

601 602
/**
 * g_buffered_input_stream_peek_buffer:
Matthias Clasen's avatar
Matthias Clasen committed
603
 * @stream: a #GBufferedInputStream
604
 * @count: (out): a #gsize to get the number of bytes available in the buffer
605 606 607 608 609
 *
 * 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.
 *
610 611
 * Returns: (array length=count) (element-type guint8) (transfer none):
 *          read-only buffer
Matthias Clasen's avatar
Matthias Clasen committed
612
 */
613
const void*
Matthias Clasen's avatar
Matthias Clasen committed
614 615
g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream,
                                     gsize                *count)
616 617 618 619 620 621 622
{
  GBufferedInputStreamPrivate *priv;

  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), NULL);

  priv = stream->priv;

Matthias Clasen's avatar
Matthias Clasen committed
623
  if (count)
624 625 626 627 628
    *count = priv->end - priv->pos;

  return priv->buffer + priv->pos;
}

629 630 631 632 633 634 635 636 637
static void
compact_buffer (GBufferedInputStream *stream)
{
  GBufferedInputStreamPrivate *priv;
  gsize current_size;

  priv = stream->priv;

  current_size = priv->end - priv->pos;
Matthias Clasen's avatar
Matthias Clasen committed
638

Dan Winship's avatar
Dan Winship committed
639
  memmove (priv->buffer, priv->buffer + priv->pos, current_size);
Matthias Clasen's avatar
Matthias Clasen committed
640

641 642 643 644 645
  priv->pos = 0;
  priv->end = current_size;
}

static gssize
Matthias Clasen's avatar
Matthias Clasen committed
646 647 648 649
g_buffered_input_stream_real_fill (GBufferedInputStream  *stream,
                                   gssize                 count,
                                   GCancellable          *cancellable,
                                   GError               **error)
650 651 652 653 654 655 656 657 658 659
{
  GBufferedInputStreamPrivate *priv;
  GInputStream *base_stream;
  gssize nread;
  gsize in_buffer;

  priv = stream->priv;

  if (count == -1)
    count = priv->len;
Matthias Clasen's avatar
Matthias Clasen committed
660

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
  in_buffer = priv->end - priv->pos;

  /* Never fill more than can fit in the buffer */
  count = MIN (count, priv->len - in_buffer);

  /* If requested length does not fit at end, compact */
  if (priv->len - priv->end < count)
    compact_buffer (stream);

  base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
  nread = g_input_stream_read (base_stream,
                               priv->buffer + priv->end,
                               count,
                               cancellable,
                               error);

  if (nread > 0)
    priv->end += nread;
Matthias Clasen's avatar
Matthias Clasen committed
679

680 681 682 683
  return nread;
}

static gssize
Matthias Clasen's avatar
Matthias Clasen committed
684 685 686 687
g_buffered_input_stream_skip (GInputStream  *stream,
                              gsize          count,
                              GCancellable  *cancellable,
                              GError       **error)
688 689 690
{
  GBufferedInputStream        *bstream;
  GBufferedInputStreamPrivate *priv;
691
  GBufferedInputStreamClass *class;
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
  GInputStream *base_stream;
  gsize available, bytes_skipped;
  gssize nread;

  bstream = G_BUFFERED_INPUT_STREAM (stream);
  priv = bstream->priv;

  available = priv->end - priv->pos;

  if (count <= available)
    {
      priv->pos += count;
      return count;
    }

Matthias Clasen's avatar
Matthias Clasen committed
707 708
  /* Full request not available, skip all currently available and
   * request refill for more
Matthias Clasen's avatar
Matthias Clasen committed
709
   */
Matthias Clasen's avatar
Matthias Clasen committed
710

711 712 713 714 715 716 717
  priv->pos = 0;
  priv->end = 0;
  bytes_skipped = available;
  count -= available;

  if (bytes_skipped > 0)
    error = NULL; /* Ignore further errors if we already read some data */
Matthias Clasen's avatar
Matthias Clasen committed
718

719 720 721
  if (count > priv->len)
    {
      /* Large request, shortcut buffer */
Matthias Clasen's avatar
Matthias Clasen committed
722

723 724 725
      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;

      nread = g_input_stream_skip (base_stream,
Matthias Clasen's avatar
Matthias Clasen committed
726 727 728
                                   count,
                                   cancellable,
                                   error);
Matthias Clasen's avatar
Matthias Clasen committed
729

730
      if (nread < 0 && bytes_skipped == 0)
Matthias Clasen's avatar
Matthias Clasen committed
731
        return -1;
Matthias Clasen's avatar
Matthias Clasen committed
732

733
      if (nread > 0)
Matthias Clasen's avatar
Matthias Clasen committed
734
        bytes_skipped += nread;
Matthias Clasen's avatar
Matthias Clasen committed
735

736 737
      return bytes_skipped;
    }
Matthias Clasen's avatar
Matthias Clasen committed
738

739 740
  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
  nread = class->fill (bstream, priv->len, cancellable, error);
Matthias Clasen's avatar
Matthias Clasen committed
741

742 743 744
  if (nread < 0)
    {
      if (bytes_skipped == 0)
Matthias Clasen's avatar
Matthias Clasen committed
745
        return -1;
746
      else
Matthias Clasen's avatar
Matthias Clasen committed
747
        return bytes_skipped;
748
    }
Matthias Clasen's avatar
Matthias Clasen committed
749

750 751
  available = priv->end - priv->pos;
  count = MIN (count, available);
Matthias Clasen's avatar
Matthias Clasen committed
752

753 754
  bytes_skipped += count;
  priv->pos += count;
Matthias Clasen's avatar
Matthias Clasen committed
755

756 757 758 759 760 761 762 763 764 765 766 767
  return bytes_skipped;
}

static gssize
g_buffered_input_stream_read (GInputStream *stream,
                              void         *buffer,
                              gsize         count,
                              GCancellable *cancellable,
                              GError      **error)
{
  GBufferedInputStream        *bstream;
  GBufferedInputStreamPrivate *priv;
768
  GBufferedInputStreamClass *class;
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
  GInputStream *base_stream;
  gsize available, bytes_read;
  gssize nread;

  bstream = G_BUFFERED_INPUT_STREAM (stream);
  priv = bstream->priv;

  available = priv->end - priv->pos;

  if (count <= available)
    {
      memcpy (buffer, priv->buffer + priv->pos, count);
      priv->pos += count;
      return count;
    }
Matthias Clasen's avatar
Matthias Clasen committed
784 785 786 787 788

  /* Full request not available, read all currently available and
   * request refill for more
   */

789 790 791 792 793 794 795 796
  memcpy (buffer, priv->buffer + priv->pos, available);
  priv->pos = 0;
  priv->end = 0;
  bytes_read = available;
  count -= available;

  if (bytes_read > 0)
    error = NULL; /* Ignore further errors if we already read some data */
Matthias Clasen's avatar
Matthias Clasen committed
797

798 799 800
  if (count > priv->len)
    {
      /* Large request, shortcut buffer */
Matthias Clasen's avatar
Matthias Clasen committed
801

802 803 804
      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;

      nread = g_input_stream_read (base_stream,
Matthias Clasen's avatar
Matthias Clasen committed
805 806 807 808 809
                                   (char *)buffer + bytes_read,
                                   count,
                                   cancellable,
                                   error);

810
      if (nread < 0 && bytes_read == 0)
Matthias Clasen's avatar
Matthias Clasen committed
811
        return -1;
Matthias Clasen's avatar
Matthias Clasen committed
812

813
      if (nread > 0)
Matthias Clasen's avatar
Matthias Clasen committed
814
        bytes_read += nread;
Matthias Clasen's avatar
Matthias Clasen committed
815

816 817
      return bytes_read;
    }
Matthias Clasen's avatar
Matthias Clasen committed
818

819 820
  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
  nread = class->fill (bstream, priv->len, cancellable, error);
821 822 823
  if (nread < 0)
    {
      if (bytes_read == 0)
Matthias Clasen's avatar
Matthias Clasen committed
824
        return -1;
825
      else
Matthias Clasen's avatar
Matthias Clasen committed
826
        return bytes_read;
827
    }
Matthias Clasen's avatar
Matthias Clasen committed
828

829 830
  available = priv->end - priv->pos;
  count = MIN (count, available);
Matthias Clasen's avatar
Matthias Clasen committed
831

832 833 834
  memcpy ((char *)buffer + bytes_read, (char *)priv->buffer + priv->pos, count);
  bytes_read += count;
  priv->pos += count;
Matthias Clasen's avatar
Matthias Clasen committed
835

836 837 838
  return bytes_read;
}

839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
static goffset
g_buffered_input_stream_tell (GSeekable *seekable)
{
  GBufferedInputStream        *bstream;
  GBufferedInputStreamPrivate *priv;
  GInputStream *base_stream;
  GSeekable    *base_stream_seekable;
  gsize available;
  goffset base_offset;
  
  bstream = G_BUFFERED_INPUT_STREAM (seekable);
  priv = bstream->priv;

  base_stream = G_FILTER_INPUT_STREAM (seekable)->base_stream;
  if (!G_IS_SEEKABLE (base_stream))
    return 0;
  base_stream_seekable = G_SEEKABLE (base_stream);
  
  available = priv->end - priv->pos;
  base_offset = g_seekable_tell (base_stream_seekable);

  return base_offset - available;
}

static gboolean
g_buffered_input_stream_can_seek (GSeekable *seekable)
{
  GInputStream *base_stream;
  
  base_stream = G_FILTER_INPUT_STREAM (seekable)->base_stream;
  return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream));
}

static gboolean
g_buffered_input_stream_seek (GSeekable     *seekable,
			      goffset        offset,
			      GSeekType      type,
			      GCancellable  *cancellable,
			      GError       **error)
{
  GBufferedInputStream        *bstream;
  GBufferedInputStreamPrivate *priv;
  GInputStream *base_stream;
  GSeekable *base_stream_seekable;

  bstream = G_BUFFERED_INPUT_STREAM (seekable);
  priv = bstream->priv;

  base_stream = G_FILTER_INPUT_STREAM (seekable)->base_stream;
  if (!G_IS_SEEKABLE (base_stream))
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                           _("Seek not supported on base stream"));
      return FALSE;
    }

  base_stream_seekable = G_SEEKABLE (base_stream);
  
  if (type == G_SEEK_CUR)
    {
      if (offset <= priv->end - priv->pos && offset >= -priv->pos)
	{
	  priv->pos += offset;
	  return TRUE;
	}
      else
	{
	  offset -= priv->end - priv->pos;
	}
    }

  if (g_seekable_seek (base_stream_seekable, offset, type, cancellable, error))
    {
      priv->pos = 0;
      priv->end = 0;
      return TRUE;
    }
  else
    {
      return FALSE;
    }
}

static gboolean
g_buffered_input_stream_can_truncate (GSeekable *seekable)
{
  return FALSE;
}

static gboolean
g_buffered_input_stream_truncate (GSeekable     *seekable,
				  goffset        offset,
				  GCancellable  *cancellable,
				  GError       **error)
{
  g_set_error_literal (error,
		       G_IO_ERROR,
		       G_IO_ERROR_NOT_SUPPORTED,
		       _("Cannot truncate GBufferedInputStream"));
  return FALSE;
}

941 942
/**
 * g_buffered_input_stream_read_byte:
Matthias Clasen's avatar
Matthias Clasen committed
943
 * @stream: a #GBufferedInputStream
944
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
Matthias Clasen's avatar
Matthias Clasen committed
945
 * @error: location to store the error occurring, or %NULL to ignore
946 947 948 949 950 951
 *
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
952
 *
953 954 955 956 957 958 959
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
960
 *
961
 * Returns: the byte read from the @stream, or -1 on end of stream or error.
Matthias Clasen's avatar
Matthias Clasen committed
962
 */
963 964 965 966 967 968
int
g_buffered_input_stream_read_byte (GBufferedInputStream  *stream,
                                   GCancellable          *cancellable,
                                   GError               **error)
{
  GBufferedInputStreamPrivate *priv;
969
  GBufferedInputStreamClass *class;
970 971 972 973 974 975 976 977 978 979 980
  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))
    {
981 982
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
                           _("Stream is already closed"));
983 984 985
      return -1;
    }

986 987
  if (!g_input_stream_set_pending (input_stream, error))
    return -1;
988 989 990

  available = priv->end - priv->pos;

991
  if (available != 0)
992 993 994 995
    {
      g_input_stream_clear_pending (input_stream);
      return priv->buffer[priv->pos++];
    }
996 997 998 999

  /* Byte not available, request refill for more */

  if (cancellable)
1000
    g_cancellable_push_current (cancellable);
1001 1002 1003 1004

  priv->pos = 0;
  priv->end = 0;

1005 1006
  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
  nread = class->fill (stream, priv->len, cancellable, error);
1007 1008

  if (cancellable)
1009
    g_cancellable_pop_current (cancellable);
1010

1011
  g_input_stream_clear_pending (input_stream);
1012

1013 1014 1015 1016 1017 1018
  if (nread <= 0)
    return -1; /* error or end of stream */

  return priv->buffer[priv->pos++];
}

1019 1020 1021 1022 1023
/* ************************** */
/* Async stuff implementation */
/* ************************** */

static void
Matthias Clasen's avatar
Matthias Clasen committed
1024 1025 1026
fill_async_callback (GObject      *source_object,
                     GAsyncResult *result,
                     gpointer      user_data)
1027 1028 1029
{
  GError *error;
  gssize res;
1030
  GTask *task = user_data;
Matthias Clasen's avatar
Matthias Clasen committed
1031

1032 1033
  error = NULL;
  res = g_input_stream_read_finish (G_INPUT_STREAM (source_object),
Matthias Clasen's avatar
Matthias Clasen committed
1034
                                    result, &error);
1035
  if (res == -1)
1036
    g_task_return_error (task, error);
1037 1038
  else
    {
1039
      GBufferedInputStream *stream;
1040 1041
      GBufferedInputStreamPrivate *priv;

1042 1043
      stream = g_task_get_source_object (task);
      priv = G_BUFFERED_INPUT_STREAM (stream)->priv;
1044 1045 1046 1047

      g_assert_cmpint (priv->end + res, <=, priv->len);
      priv->end += res;

1048
      g_task_return_int (task, res);
1049
    }
Matthias Clasen's avatar
Matthias Clasen committed
1050

1051
  g_object_unref (task);
1052 1053 1054
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
1055 1056 1057 1058 1059 1060
g_buffered_input_stream_real_fill_async (GBufferedInputStream *stream,
                                         gssize                count,
                                         int                   io_priority,
                                         GCancellable         *cancellable,
                                         GAsyncReadyCallback   callback,
                                         gpointer              user_data)
1061 1062 1063
{
  GBufferedInputStreamPrivate *priv;
  GInputStream *base_stream;
1064
  GTask *task;
1065 1066 1067 1068 1069 1070
  gsize in_buffer;

  priv = stream->priv;

  if (count == -1)
    count = priv->len;
Matthias Clasen's avatar
Matthias Clasen committed
1071

1072 1073 1074 1075 1076 1077 1078 1079 1080
  in_buffer = priv->end - priv->pos;

  /* Never fill more than can fit in the buffer */
  count = MIN (count, priv->len - in_buffer);

  /* If requested length does not fit at end, compact */
  if (priv->len - priv->end < count)
    compact_buffer (stream);

1081
  task = g_task_new (stream, cancellable, callback, user_data);
1082
  g_task_set_source_tag (task, g_buffered_input_stream_real_fill_async);
Matthias Clasen's avatar
Matthias Clasen committed
1083

1084 1085
  base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
  g_input_stream_read_async (base_stream,
Matthias Clasen's avatar
Matthias Clasen committed
1086 1087 1088 1089 1090
                             priv->buffer + priv->end,
                             count,
                             io_priority,
                             cancellable,
                             fill_async_callback,
1091
                             task);
1092 1093 1094 1095
}

static gssize
g_buffered_input_stream_real_fill_finish (GBufferedInputStream *stream,
Matthias Clasen's avatar
Matthias Clasen committed
1096 1097
                                          GAsyncResult         *result,
                                          GError              **error)
1098
{
1099
  g_return_val_if_fail (g_task_is_valid (result, stream), -1);
1100

1101
  return g_task_propagate_int (G_TASK (result), error);
1102 1103
}

Matthias Clasen's avatar
Matthias Clasen committed
1104 1105
typedef struct
{
1106 1107 1108 1109
  gssize bytes_skipped;
  gssize count;
} SkipAsyncData;

Matthias Clasen's avatar
Matthias Clasen committed
1110
static void
1111 1112 1113 1114 1115 1116 1117
free_skip_async_data (gpointer _data)
{
  SkipAsyncData *data = _data;
  g_slice_free (SkipAsyncData, data);
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
1118 1119 1120
large_skip_callback (GObject      *source_object,
                     GAsyncResult *result,
                     gpointer      user_data)
1121
{
1122
  GTask *task = G_TASK (user_data);
1123 1124 1125 1126
  SkipAsyncData *data;
  GError *error;
  gssize nread;

1127
  data = g_task_get_task_data (task);
Matthias Clasen's avatar
Matthias Clasen committed
1128

1129 1130
  error = NULL;
  nread = g_input_stream_skip_finish (G_INPUT_STREAM (source_object),
Matthias Clasen's avatar
Matthias Clasen committed
1131
                                      result, &error);
1132 1133 1134

  /* Only report the error if we've not already read some data */
  if (nread < 0 && data->bytes_skipped == 0)
1135 1136 1137 1138 1139
    g_task_return_error (task, error);
  else
    {
      if (error)
	g_error_free (error);
Matthias Clasen's avatar
Matthias Clasen committed
1140

1141 1142
      if (nread > 0)
	data->bytes_skipped += nread;
Matthias Clasen's avatar
Matthias Clasen committed
1143

1144 1145 1146 1147
      g_task_return_int (task, data->bytes_skipped);
    }

  g_object_unref (task);
1148 1149 1150
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
1151 1152 1153
skip_fill_buffer_callback (GObject      *source_object,
                           GAsyncResult *result,
                           gpointer      user_data)
1154
{
1155
  GTask *task = G_TASK (user_data);
1156 1157 1158 1159 1160 1161 1162 1163 1164
  GBufferedInputStream *bstream;
  GBufferedInputStreamPrivate *priv;
  SkipAsyncData *data;
  GError *error;
  gssize nread;
  gsize available;

  bstream = G_BUFFERED_INPUT_STREAM (source_object);
  priv = bstream->priv;
Matthias Clasen's avatar
Matthias Clasen committed
1165

1166
  data = g_task_get_task_data (task);
Matthias Clasen's avatar
Matthias Clasen committed
1167

1168 1169
  error = NULL;
  nread = g_buffered_input_stream_fill_finish (bstream,
Matthias Clasen's avatar
Matthias Clasen committed
1170 1171
                                               result, &error);

1172
  if (nread < 0 && data->bytes_skipped == 0)
1173 1174
    g_task_return_error (task, error);
  else
1175
    {
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
      if (error)
	g_error_free (error);

      if (nread > 0)
	{
	  available = priv->end - priv->pos;
	  data->count = MIN (data->count, available);

	  data->bytes_skipped += data->count;
	  priv->pos += data->count;
	}
Matthias Clasen's avatar
Matthias Clasen committed
1187

1188
      g_task_return_int (task, data->bytes_skipped);
1189 1190
    }

1191
  g_object_unref (task);
1192 1193 1194
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
1195 1196 1197 1198 1199 1200
g_buffered_input_stream_skip_async (GInputStream        *stream,
                                    gsize                count,
                                    int                  io_priority,
                                    GCancellable        *cancellable,
                                    GAsyncReadyCallback  callback,
                                    gpointer             user_data)
1201 1202 1203
{
  GBufferedInputStream *bstream;
  GBufferedInputStreamPrivate *priv;
1204
  GBufferedInputStreamClass *class;
1205 1206
  GInputStream *base_stream;
  gsize available;
1207
  GTask *task;
1208 1209 1210 1211 1212 1213 1214
  SkipAsyncData *data;

  bstream = G_BUFFERED_INPUT_STREAM (stream);
  priv = bstream->priv;

  data = g_slice_new (SkipAsyncData);
  data->bytes_skipped = 0;
1215
  task = g_task_new (stream, cancellable, callback, user_data);
1216
  g_task_set_source_tag (task, g_buffered_input_stream_skip_async);
1217
  g_task_set_task_data (task, data, free_skip_async_data);
Matthias Clasen's avatar
Matthias Clasen committed
1218

1219
  available = priv->end - priv->pos;
Matthias Clasen's avatar
Matthias Clasen committed
1220

1221 1222 1223
  if (count <= available)
    {
      priv->pos += count;
Matthias Clasen's avatar
Matthias Clasen committed
1224

1225 1226
      g_task_return_int (task, count);
      g_object_unref (task);
1227 1228 1229
      return;
    }

Matthias Clasen's avatar
Matthias Clasen committed
1230 1231 1232
  /* Full request not available, skip all currently available
   * and request refill for more
   */
1233 1234 1235

  priv->pos = 0;
  priv->end = 0;
Matthias Clasen's avatar
Matthias Clasen committed
1236

1237
  count -= available;
Matthias Clasen's avatar
Matthias Clasen committed
1238

1239 1240 1241 1242 1243 1244
  data->bytes_skipped = available;
  data->count = count;

  if (count > priv->len)
    {
      /* Large request, shortcut buffer */
Matthias Clasen's avatar
Matthias Clasen committed
1245

1246
      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
Matthias Clasen's avatar
Matthias Clasen committed
1247

1248
      g_input_stream_skip_async (base_stream,
Matthias Clasen's avatar
Matthias Clasen committed
1249 1250 1251
                                 count,
                                 io_priority, cancellable,
                                 large_skip_callback,
1252
                                 task);
1253 1254 1255
    }
  else
    {
1256 1257
      class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
      class->fill_async (bstream, priv->len, io_priority, cancellable,
1258
                         skip_fill_buffer_callback, task);
1259 1260 1261 1262 1263 1264 1265 1266
    }
}

static gssize
g_buffered_input_stream_skip_finish (GInputStream   *stream,
                                     GAsyncResult   *result,
                                     GError        **error)
{
1267
  g_return_val_if_fail (g_task_is_valid (result, stream), -1);
Matthias Clasen's avatar
Matthias Clasen committed
1268

1269
  return g_task_propagate_int (G_TASK (result), error);
1270
}