gmemoryinputstream.c 15.8 KB
Newer Older
1 2 3 4 5 6 7
/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * 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
8
 * version 2.1 of the License, or (at your option) any later version.
9 10 11 12 13 14 15
 *
 * 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
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 18 19 20
 *
 * Author: Christian Kellner <gicmo@gnome.org> 
 */

21
#include "config.h"
22
#include "gmemoryinputstream.h"
23
#include "gpollableinputstream.h"
24 25 26
#include "ginputstream.h"
#include "gseekable.h"
#include "string.h"
27
#include "gtask.h"
28
#include "gioerror.h"
29 30
#include "glibintl.h"

31

32 33
/**
 * SECTION:gmemoryinputstream
Matthias Clasen's avatar
Matthias Clasen committed
34
 * @short_description: Streaming input operations on memory chunks
Matthias Clasen's avatar
Matthias Clasen committed
35
 * @include: gio/gio.h
36
 * @see_also: #GMemoryOutputStream
37 38 39 40
 *
 * #GMemoryInputStream is a class for using arbitrary
 * memory chunks as input for GIO streaming input operations.
 *
41 42
 * As of GLib 2.34, #GMemoryInputStream implements
 * #GPollableInputStream.
43 44
 */

45
struct _GMemoryInputStreamPrivate {
46
  GSList *chunks;
47
  gsize   len;
48
  gsize   pos;
49 50 51 52 53 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 85 86 87 88 89 90 91 92 93
};

static gssize   g_memory_input_stream_read         (GInputStream         *stream,
						    void                 *buffer,
						    gsize                 count,
						    GCancellable         *cancellable,
						    GError              **error);
static gssize   g_memory_input_stream_skip         (GInputStream         *stream,
						    gsize                 count,
						    GCancellable         *cancellable,
						    GError              **error);
static gboolean g_memory_input_stream_close        (GInputStream         *stream,
						    GCancellable         *cancellable,
						    GError              **error);
static void     g_memory_input_stream_skip_async   (GInputStream         *stream,
						    gsize                 count,
						    int                   io_priority,
						    GCancellable         *cancellabl,
						    GAsyncReadyCallback   callback,
						    gpointer              datae);
static gssize   g_memory_input_stream_skip_finish  (GInputStream         *stream,
						    GAsyncResult         *result,
						    GError              **error);
static void     g_memory_input_stream_close_async  (GInputStream         *stream,
						    int                   io_priority,
						    GCancellable         *cancellabl,
						    GAsyncReadyCallback   callback,
						    gpointer              data);
static gboolean g_memory_input_stream_close_finish (GInputStream         *stream,
						    GAsyncResult         *result,
						    GError              **error);

static void     g_memory_input_stream_seekable_iface_init (GSeekableIface  *iface);
static goffset  g_memory_input_stream_tell                (GSeekable       *seekable);
static gboolean g_memory_input_stream_can_seek            (GSeekable       *seekable);
static gboolean g_memory_input_stream_seek                (GSeekable       *seekable,
                                                           goffset          offset,
                                                           GSeekType        type,
                                                           GCancellable    *cancellable,
                                                           GError         **error);
static gboolean g_memory_input_stream_can_truncate        (GSeekable       *seekable);
static gboolean g_memory_input_stream_truncate            (GSeekable       *seekable,
                                                           goffset          offset,
                                                           GCancellable    *cancellable,
                                                           GError         **error);
94 95 96 97 98 99

static void     g_memory_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
static gboolean g_memory_input_stream_is_readable         (GPollableInputStream *stream);
static GSource *g_memory_input_stream_create_source       (GPollableInputStream *stream,
							   GCancellable          *cancellable);

100 101 102
static void     g_memory_input_stream_finalize            (GObject         *object);

G_DEFINE_TYPE_WITH_CODE (GMemoryInputStream, g_memory_input_stream, G_TYPE_INPUT_STREAM,
103
                         G_ADD_PRIVATE (GMemoryInputStream)
104
                         G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
105 106 107 108
                                                g_memory_input_stream_seekable_iface_init);
                         G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
                                                g_memory_input_stream_pollable_iface_init);
			 )
109 110 111 112 113 114 115 116 117 118 119 120


static void
g_memory_input_stream_class_init (GMemoryInputStreamClass *klass)
{
  GObjectClass *object_class;
  GInputStreamClass *istream_class;

  object_class = G_OBJECT_CLASS (klass);
  object_class->finalize     = g_memory_input_stream_finalize;
  
  istream_class = G_INPUT_STREAM_CLASS (klass);
121
  istream_class->read_fn  = g_memory_input_stream_read;
122
  istream_class->skip  = g_memory_input_stream_skip;
123
  istream_class->close_fn = g_memory_input_stream_close;
124 125 126 127 128 129 130 131 132 133 134

  istream_class->skip_async  = g_memory_input_stream_skip_async;
  istream_class->skip_finish  = g_memory_input_stream_skip_finish;
  istream_class->close_async = g_memory_input_stream_close_async;
  istream_class->close_finish = g_memory_input_stream_close_finish;
}

static void
g_memory_input_stream_finalize (GObject *object)
{
  GMemoryInputStream        *stream;
135
  GMemoryInputStreamPrivate *priv;
136 137

  stream = G_MEMORY_INPUT_STREAM (object);
138
  priv = stream->priv;
139

140
  g_slist_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref);
141

142
  G_OBJECT_CLASS (g_memory_input_stream_parent_class)->finalize (object);
143 144 145 146 147 148 149 150 151
}

static void
g_memory_input_stream_seekable_iface_init (GSeekableIface *iface)
{
  iface->tell         = g_memory_input_stream_tell;
  iface->can_seek     = g_memory_input_stream_can_seek;
  iface->seek         = g_memory_input_stream_seek;
  iface->can_truncate = g_memory_input_stream_can_truncate;
152
  iface->truncate_fn  = g_memory_input_stream_truncate;
153 154
}

155 156 157 158 159 160 161
static void
g_memory_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
{
  iface->is_readable   = g_memory_input_stream_is_readable;
  iface->create_source = g_memory_input_stream_create_source;
}

162 163 164
static void
g_memory_input_stream_init (GMemoryInputStream *stream)
{
165
  stream->priv = g_memory_input_stream_get_instance_private (stream);
166 167
}

Matthias Clasen's avatar
Matthias Clasen committed
168 169 170 171 172 173 174
/**
 * g_memory_input_stream_new:
 *
 * Creates a new empty #GMemoryInputStream. 
 *
 * Returns: a new #GInputStream
 */
175 176
GInputStream *
g_memory_input_stream_new (void)
177
{
178 179 180
  GInputStream *stream;

  stream = g_object_new (G_TYPE_MEMORY_INPUT_STREAM, NULL);
181

182
  return stream;
183 184 185
}

/**
186
 * g_memory_input_stream_new_from_data:
187
 * @data: (array length=len) (element-type guint8) (transfer full): input data
188
 * @len: length of the data, may be -1 if @data is a nul-terminated string
189
 * @destroy: (nullable): function that is called to free @data, or %NULL
190 191
 *
 * Creates a new #GMemoryInputStream with data in memory of a given size.
192 193 194 195
 * 
 * Returns: new #GInputStream read from @data of @len bytes.
 **/
GInputStream *
196 197 198
g_memory_input_stream_new_from_data (const void     *data, 
                                     gssize          len,
                                     GDestroyNotify  destroy)
199 200 201
{
  GInputStream *stream;

202
  stream = g_memory_input_stream_new ();
203

204 205
  g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
                                  data, len, destroy);
206 207 208 209

  return stream;
}

210 211 212 213 214
/**
 * g_memory_input_stream_new_from_bytes:
 * @bytes: a #GBytes
 *
 * Creates a new #GMemoryInputStream with data from the given @bytes.
215
 *
216
 * Returns: new #GInputStream read from @bytes
217
 *
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
 * Since: 2.34
 **/
GInputStream *
g_memory_input_stream_new_from_bytes (GBytes  *bytes)
{
  
  GInputStream *stream;

  stream = g_memory_input_stream_new ();

  g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream),
				   bytes);

  return stream;
}

Matthias Clasen's avatar
Matthias Clasen committed
234 235 236
/**
 * g_memory_input_stream_add_data:
 * @stream: a #GMemoryInputStream
237
 * @data: (array length=len) (element-type guint8) (transfer full): input data
Matthias Clasen's avatar
Matthias Clasen committed
238
 * @len: length of the data, may be -1 if @data is a nul-terminated string
239
 * @destroy: (nullable): function that is called to free @data, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
240 241 242
 *
 * Appends @data to data that can be read from the input stream
 */
243 244 245 246 247
void
g_memory_input_stream_add_data (GMemoryInputStream *stream,
                                const void         *data,
                                gssize              len,
                                GDestroyNotify      destroy)
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
{
  GBytes *bytes;

  if (len == -1)
    len = strlen (data);

  /* It's safe to discard the const here because we're chaining the
   * destroy callback.
   */
  bytes = g_bytes_new_with_free_func (data, len, destroy, (void*)data);

  g_memory_input_stream_add_bytes (stream, bytes);
  
  g_bytes_unref (bytes);
}

/**
 * g_memory_input_stream_add_bytes:
 * @stream: a #GMemoryInputStream
 * @bytes: input data
 *
 * Appends @bytes to data that can be read from the input stream.
 *
 * Since: 2.34
 */
void
g_memory_input_stream_add_bytes (GMemoryInputStream *stream,
				 GBytes             *bytes)
276 277 278 279
{
  GMemoryInputStreamPrivate *priv;
 
  g_return_if_fail (G_IS_MEMORY_INPUT_STREAM (stream));
280
  g_return_if_fail (bytes != NULL);
281 282

  priv = stream->priv;
283

284 285
  priv->chunks = g_slist_append (priv->chunks, g_bytes_ref (bytes));
  priv->len += g_bytes_get_size (bytes);
286 287 288
}

static gssize
289 290 291 292 293
g_memory_input_stream_read (GInputStream  *stream,
                            void          *buffer,
                            gsize          count,
                            GCancellable  *cancellable,
                            GError       **error)
294 295
{
  GMemoryInputStream *memory_stream;
296 297
  GMemoryInputStreamPrivate *priv;
  GSList *l;
298 299
  GBytes *chunk;
  gsize len;
300
  gsize offset, start, rest, size;
301 302 303 304 305 306

  memory_stream = G_MEMORY_INPUT_STREAM (stream);
  priv = memory_stream->priv;

  count = MIN (count, priv->len - priv->pos);

307 308 309
  offset = 0;
  for (l = priv->chunks; l; l = l->next) 
    {
310 311
      chunk = (GBytes *)l->data;
      len = g_bytes_get_size (chunk);
312

313
      if (offset + len > priv->pos)
314
        break;
315

316
      offset += len;
317 318 319 320
    }
  
  start = priv->pos - offset;
  rest = count;
321

322 323
  for (; l && rest > 0; l = l->next)
    {
324 325 326 327 328 329
      const guint8* chunk_data;
      chunk = (GBytes *)l->data;

      chunk_data = g_bytes_get_data (chunk, &len);

      size = MIN (rest, len - start);
330

331
      memcpy ((guint8 *)buffer + (count - rest), chunk_data + start, size);
332 333 334 335 336 337
      rest -= size;

      start = 0;
    }

  priv->pos += count;
338

339
  return count;
340 341 342
}

static gssize
343 344 345 346
g_memory_input_stream_skip (GInputStream  *stream,
                            gsize          count,
                            GCancellable  *cancellable,
                            GError       **error)
347 348 349 350 351 352 353 354 355 356 357 358 359 360
{
  GMemoryInputStream *memory_stream;
  GMemoryInputStreamPrivate *priv;

  memory_stream = G_MEMORY_INPUT_STREAM (stream);
  priv = memory_stream->priv;

  count = MIN (count, priv->len - priv->pos);
  priv->pos += count;

  return count;
}

static gboolean
361 362 363
g_memory_input_stream_close (GInputStream  *stream,
                             GCancellable  *cancellable,
                             GError       **error)
364 365 366 367 368
{
  return TRUE;
}

static void
369 370 371 372 373 374
g_memory_input_stream_skip_async (GInputStream        *stream,
                                  gsize                count,
                                  int                  io_priority,
                                  GCancellable        *cancellable,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
375
{
376
  GTask *task;
377
  gssize nskipped;
378
  GError *error = NULL;
379

380
  nskipped = G_INPUT_STREAM_GET_CLASS (stream)->skip (stream, count, cancellable, &error);
381
  task = g_task_new (stream, cancellable, callback, user_data);
382 383
  g_task_set_source_tag (task, g_memory_input_stream_skip_async);

384 385 386 387
  if (error)
    g_task_return_error (task, error);
  else
    g_task_return_int (task, nskipped);
388
  g_object_unref (task);
389 390 391
}

static gssize
392 393 394
g_memory_input_stream_skip_finish (GInputStream  *stream,
                                   GAsyncResult  *result,
                                   GError       **error)
395
{
396
  g_return_val_if_fail (g_task_is_valid (result, stream), -1);
397

398
  return g_task_propagate_int (G_TASK (result), error);
399 400 401
}

static void
402 403 404 405 406
g_memory_input_stream_close_async (GInputStream        *stream,
                                   int                  io_priority,
                                   GCancellable        *cancellable,
                                   GAsyncReadyCallback  callback,
                                   gpointer             user_data)
407
{
408 409 410
  GTask *task;

  task = g_task_new (stream, cancellable, callback, user_data);
411
  g_task_set_source_tag (task, g_memory_input_stream_close_async);
412 413
  g_task_return_boolean (task, TRUE);
  g_object_unref (task);
414 415 416
}

static gboolean
417 418 419
g_memory_input_stream_close_finish (GInputStream  *stream,
                                    GAsyncResult  *result,
                                    GError       **error)
420 421 422 423 424 425 426 427
{
  return TRUE;
}

static goffset
g_memory_input_stream_tell (GSeekable *seekable)
{
  GMemoryInputStream *memory_stream;
428
  GMemoryInputStreamPrivate *priv;
429 430 431 432 433 434 435 436 437 438 439 440 441 442

  memory_stream = G_MEMORY_INPUT_STREAM (seekable);
  priv = memory_stream->priv;

  return priv->pos;
}

static
gboolean g_memory_input_stream_can_seek (GSeekable *seekable)
{
  return TRUE;
}

static gboolean
443 444 445 446 447
g_memory_input_stream_seek (GSeekable     *seekable,
                            goffset        offset,
                            GSeekType      type,
                            GCancellable  *cancellable,
                            GError       **error)
448 449
{
  GMemoryInputStream *memory_stream;
450
  GMemoryInputStreamPrivate *priv;
451 452 453 454 455
  goffset absolute;

  memory_stream = G_MEMORY_INPUT_STREAM (seekable);
  priv = memory_stream->priv;

456 457
  switch (type) 
    {
458 459 460 461 462 463 464 465 466 467 468 469 470
    case G_SEEK_CUR:
      absolute = priv->pos + offset;
      break;

    case G_SEEK_SET:
      absolute = offset;
      break;

    case G_SEEK_END:
      absolute = priv->len + offset;
      break;
  
    default:
471 472 473 474
      g_set_error_literal (error,
                           G_IO_ERROR,
                           G_IO_ERROR_INVALID_ARGUMENT,
                           _("Invalid GSeekType supplied"));
475 476

      return FALSE;
477
    }
478 479 480

  if (absolute < 0 || absolute > priv->len)
    {
481 482 483 484
      g_set_error_literal (error,
                           G_IO_ERROR,
                           G_IO_ERROR_INVALID_ARGUMENT,
                           _("Invalid seek request"));
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
      return FALSE;
    }

  priv->pos = absolute;

  return TRUE;
}

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

static gboolean
500 501 502 503
g_memory_input_stream_truncate (GSeekable     *seekable,
                                goffset        offset,
                                GCancellable  *cancellable,
                                GError       **error)
504
{
505 506 507 508
  g_set_error_literal (error,
                       G_IO_ERROR,
                       G_IO_ERROR_NOT_SUPPORTED,
                       _("Cannot truncate GMemoryInputStream"));
509 510
  return FALSE;
}
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530

static gboolean
g_memory_input_stream_is_readable (GPollableInputStream *stream)
{
  return TRUE;
}

static GSource *
g_memory_input_stream_create_source (GPollableInputStream *stream,
				     GCancellable         *cancellable)
{
  GSource *base_source, *pollable_source;

  base_source = g_timeout_source_new (0);
  pollable_source = g_pollable_source_new_full (stream, base_source,
						cancellable);
  g_source_unref (base_source);

  return pollable_source;
}