1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
3 | * Copyright (C) 2009 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * 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 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * 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 | * Author: Alexander Larsson <alexl@redhat.com> |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <string.h> |
24 | |
25 | #include "gconverterinputstream.h" |
26 | #include "gpollableinputstream.h" |
27 | #include "gcancellable.h" |
28 | #include "gioenumtypes.h" |
29 | #include "gioerror.h" |
30 | #include "glibintl.h" |
31 | |
32 | |
33 | /** |
34 | * SECTION:gconverterinputstream |
35 | * @short_description: Converter Input Stream |
36 | * @include: gio/gio.h |
37 | * @see_also: #GInputStream, #GConverter |
38 | * |
39 | * Converter input stream implements #GInputStream and allows |
40 | * conversion of data of various types during reading. |
41 | * |
42 | * As of GLib 2.34, #GConverterInputStream implements |
43 | * #GPollableInputStream. |
44 | **/ |
45 | |
46 | #define INITIAL_BUFFER_SIZE 4096 |
47 | |
48 | typedef struct { |
49 | char *data; |
50 | gsize start; |
51 | gsize end; |
52 | gsize size; |
53 | } Buffer; |
54 | |
55 | struct _GConverterInputStreamPrivate { |
56 | gboolean at_input_end; |
57 | gboolean finished; |
58 | gboolean need_input; |
59 | GConverter *converter; |
60 | Buffer input_buffer; |
61 | Buffer converted_buffer; |
62 | }; |
63 | |
64 | enum { |
65 | PROP_0, |
66 | PROP_CONVERTER |
67 | }; |
68 | |
69 | static void g_converter_input_stream_set_property (GObject *object, |
70 | guint prop_id, |
71 | const GValue *value, |
72 | GParamSpec *pspec); |
73 | static void g_converter_input_stream_get_property (GObject *object, |
74 | guint prop_id, |
75 | GValue *value, |
76 | GParamSpec *pspec); |
77 | static void g_converter_input_stream_finalize (GObject *object); |
78 | static gssize g_converter_input_stream_read (GInputStream *stream, |
79 | void *buffer, |
80 | gsize count, |
81 | GCancellable *cancellable, |
82 | GError **error); |
83 | |
84 | static gboolean g_converter_input_stream_can_poll (GPollableInputStream *stream); |
85 | static gboolean g_converter_input_stream_is_readable (GPollableInputStream *stream); |
86 | static gssize g_converter_input_stream_read_nonblocking (GPollableInputStream *stream, |
87 | void *buffer, |
88 | gsize size, |
89 | GError **error); |
90 | |
91 | static GSource *g_converter_input_stream_create_source (GPollableInputStream *stream, |
92 | GCancellable *cancellable); |
93 | |
94 | static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface); |
95 | |
96 | G_DEFINE_TYPE_WITH_CODE (GConverterInputStream, |
97 | g_converter_input_stream, |
98 | G_TYPE_FILTER_INPUT_STREAM, |
99 | G_ADD_PRIVATE (GConverterInputStream) |
100 | G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, |
101 | g_converter_input_stream_pollable_iface_init)) |
102 | |
103 | static void |
104 | g_converter_input_stream_class_init (GConverterInputStreamClass *klass) |
105 | { |
106 | GObjectClass *object_class; |
107 | GInputStreamClass *istream_class; |
108 | |
109 | object_class = G_OBJECT_CLASS (klass); |
110 | object_class->get_property = g_converter_input_stream_get_property; |
111 | object_class->set_property = g_converter_input_stream_set_property; |
112 | object_class->finalize = g_converter_input_stream_finalize; |
113 | |
114 | istream_class = G_INPUT_STREAM_CLASS (klass); |
115 | istream_class->read_fn = g_converter_input_stream_read; |
116 | |
117 | g_object_class_install_property (oclass: object_class, |
118 | property_id: PROP_CONVERTER, |
119 | pspec: g_param_spec_object (name: "converter" , |
120 | P_("Converter" ), |
121 | P_("The converter object" ), |
122 | G_TYPE_CONVERTER, |
123 | flags: G_PARAM_READWRITE| |
124 | G_PARAM_CONSTRUCT_ONLY| |
125 | G_PARAM_STATIC_STRINGS)); |
126 | |
127 | } |
128 | |
129 | static void |
130 | g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface) |
131 | { |
132 | iface->can_poll = g_converter_input_stream_can_poll; |
133 | iface->is_readable = g_converter_input_stream_is_readable; |
134 | iface->read_nonblocking = g_converter_input_stream_read_nonblocking; |
135 | iface->create_source = g_converter_input_stream_create_source; |
136 | } |
137 | |
138 | static void |
139 | g_converter_input_stream_finalize (GObject *object) |
140 | { |
141 | GConverterInputStreamPrivate *priv; |
142 | GConverterInputStream *stream; |
143 | |
144 | stream = G_CONVERTER_INPUT_STREAM (object); |
145 | priv = stream->priv; |
146 | |
147 | g_free (mem: priv->input_buffer.data); |
148 | g_free (mem: priv->converted_buffer.data); |
149 | if (priv->converter) |
150 | g_object_unref (object: priv->converter); |
151 | |
152 | G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object); |
153 | } |
154 | |
155 | static void |
156 | g_converter_input_stream_set_property (GObject *object, |
157 | guint prop_id, |
158 | const GValue *value, |
159 | GParamSpec *pspec) |
160 | { |
161 | GConverterInputStream *cstream; |
162 | |
163 | cstream = G_CONVERTER_INPUT_STREAM (object); |
164 | |
165 | switch (prop_id) |
166 | { |
167 | case PROP_CONVERTER: |
168 | cstream->priv->converter = g_value_dup_object (value); |
169 | break; |
170 | |
171 | default: |
172 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
173 | break; |
174 | } |
175 | |
176 | } |
177 | |
178 | static void |
179 | g_converter_input_stream_get_property (GObject *object, |
180 | guint prop_id, |
181 | GValue *value, |
182 | GParamSpec *pspec) |
183 | { |
184 | GConverterInputStreamPrivate *priv; |
185 | GConverterInputStream *cstream; |
186 | |
187 | cstream = G_CONVERTER_INPUT_STREAM (object); |
188 | priv = cstream->priv; |
189 | |
190 | switch (prop_id) |
191 | { |
192 | case PROP_CONVERTER: |
193 | g_value_set_object (value, v_object: priv->converter); |
194 | break; |
195 | |
196 | default: |
197 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
198 | break; |
199 | } |
200 | |
201 | } |
202 | static void |
203 | g_converter_input_stream_init (GConverterInputStream *stream) |
204 | { |
205 | stream->priv = g_converter_input_stream_get_instance_private (self: stream); |
206 | } |
207 | |
208 | /** |
209 | * g_converter_input_stream_new: |
210 | * @base_stream: a #GInputStream |
211 | * @converter: a #GConverter |
212 | * |
213 | * Creates a new converter input stream for the @base_stream. |
214 | * |
215 | * Returns: a new #GInputStream. |
216 | **/ |
217 | GInputStream * |
218 | g_converter_input_stream_new (GInputStream *base_stream, |
219 | GConverter *converter) |
220 | { |
221 | GInputStream *stream; |
222 | |
223 | g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL); |
224 | |
225 | stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM, |
226 | first_property_name: "base-stream" , base_stream, |
227 | "converter" , converter, |
228 | NULL); |
229 | |
230 | return stream; |
231 | } |
232 | |
233 | static gsize |
234 | buffer_data_size (Buffer *buffer) |
235 | { |
236 | return buffer->end - buffer->start; |
237 | } |
238 | |
239 | static gsize |
240 | buffer_tailspace (Buffer *buffer) |
241 | { |
242 | return buffer->size - buffer->end; |
243 | } |
244 | |
245 | static char * |
246 | buffer_data (Buffer *buffer) |
247 | { |
248 | return buffer->data + buffer->start; |
249 | } |
250 | |
251 | static void |
252 | buffer_consumed (Buffer *buffer, |
253 | gsize count) |
254 | { |
255 | buffer->start += count; |
256 | if (buffer->start == buffer->end) |
257 | buffer->start = buffer->end = 0; |
258 | } |
259 | |
260 | static void |
261 | buffer_read (Buffer *buffer, |
262 | char *dest, |
263 | gsize count) |
264 | { |
265 | if (count != 0) |
266 | memcpy (dest: dest, src: buffer->data + buffer->start, n: count); |
267 | |
268 | buffer_consumed (buffer, count); |
269 | } |
270 | |
271 | static void |
272 | compact_buffer (Buffer *buffer) |
273 | { |
274 | gsize in_buffer; |
275 | |
276 | in_buffer = buffer_data_size (buffer); |
277 | memmove (dest: buffer->data, |
278 | src: buffer->data + buffer->start, |
279 | n: in_buffer); |
280 | buffer->end -= buffer->start; |
281 | buffer->start = 0; |
282 | } |
283 | |
284 | static void |
285 | grow_buffer (Buffer *buffer) |
286 | { |
287 | char *data; |
288 | gsize size, in_buffer; |
289 | |
290 | if (buffer->size == 0) |
291 | size = INITIAL_BUFFER_SIZE; |
292 | else |
293 | size = buffer->size * 2; |
294 | |
295 | data = g_malloc (n_bytes: size); |
296 | in_buffer = buffer_data_size (buffer); |
297 | |
298 | if (in_buffer != 0) |
299 | memcpy (dest: data, |
300 | src: buffer->data + buffer->start, |
301 | n: in_buffer); |
302 | |
303 | g_free (mem: buffer->data); |
304 | buffer->data = data; |
305 | buffer->end -= buffer->start; |
306 | buffer->start = 0; |
307 | buffer->size = size; |
308 | } |
309 | |
310 | /* Ensures that the buffer can fit at_least_size bytes, |
311 | * *including* the current in-buffer data */ |
312 | static void |
313 | buffer_ensure_space (Buffer *buffer, |
314 | gsize at_least_size) |
315 | { |
316 | gsize in_buffer, left_to_fill; |
317 | |
318 | in_buffer = buffer_data_size (buffer); |
319 | |
320 | if (in_buffer >= at_least_size) |
321 | return; |
322 | |
323 | left_to_fill = buffer_tailspace (buffer); |
324 | |
325 | if (in_buffer + left_to_fill >= at_least_size) |
326 | { |
327 | /* We fit in remaining space at end */ |
328 | /* If the copy is small, compact now anyway so we can fill more */ |
329 | if (in_buffer < 256) |
330 | compact_buffer (buffer); |
331 | } |
332 | else if (buffer->size >= at_least_size) |
333 | { |
334 | /* We fit, but only if we compact */ |
335 | compact_buffer (buffer); |
336 | } |
337 | else |
338 | { |
339 | /* Need to grow buffer */ |
340 | while (buffer->size < at_least_size) |
341 | grow_buffer (buffer); |
342 | } |
343 | } |
344 | |
345 | static gssize |
346 | fill_input_buffer (GConverterInputStream *stream, |
347 | gsize at_least_size, |
348 | gboolean blocking, |
349 | GCancellable *cancellable, |
350 | GError **error) |
351 | { |
352 | GConverterInputStreamPrivate *priv; |
353 | GInputStream *base_stream; |
354 | gssize nread; |
355 | |
356 | priv = stream->priv; |
357 | |
358 | buffer_ensure_space (buffer: &priv->input_buffer, at_least_size); |
359 | |
360 | base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
361 | nread = g_pollable_stream_read (stream: base_stream, |
362 | buffer: priv->input_buffer.data + priv->input_buffer.end, |
363 | count: buffer_tailspace (buffer: &priv->input_buffer), |
364 | blocking, |
365 | cancellable, |
366 | error); |
367 | |
368 | if (nread > 0) |
369 | { |
370 | priv->input_buffer.end += nread; |
371 | priv->need_input = FALSE; |
372 | } |
373 | |
374 | return nread; |
375 | } |
376 | |
377 | |
378 | static gssize |
379 | read_internal (GInputStream *stream, |
380 | void *buffer, |
381 | gsize count, |
382 | gboolean blocking, |
383 | GCancellable *cancellable, |
384 | GError **error) |
385 | { |
386 | GConverterInputStream *cstream; |
387 | GConverterInputStreamPrivate *priv; |
388 | gsize available, total_bytes_read; |
389 | gssize nread; |
390 | GConverterResult res; |
391 | gsize bytes_read; |
392 | gsize bytes_written; |
393 | GError *my_error; |
394 | GError *my_error2; |
395 | |
396 | cstream = G_CONVERTER_INPUT_STREAM (stream); |
397 | priv = cstream->priv; |
398 | |
399 | available = buffer_data_size (buffer: &priv->converted_buffer); |
400 | |
401 | if (available > 0 && |
402 | count <= available) |
403 | { |
404 | /* Converted data available, return that */ |
405 | buffer_read (buffer: &priv->converted_buffer, dest: buffer, count); |
406 | return count; |
407 | } |
408 | |
409 | /* Full request not available, read all currently available and request |
410 | refill/conversion for more */ |
411 | |
412 | buffer_read (buffer: &priv->converted_buffer, dest: buffer, count: available); |
413 | |
414 | total_bytes_read = available; |
415 | buffer = (char *) buffer + available; |
416 | count -= available; |
417 | |
418 | /* If there is no data to convert, and no pre-converted data, |
419 | do some i/o for more input */ |
420 | if (buffer_data_size (buffer: &priv->input_buffer) == 0 && |
421 | total_bytes_read == 0 && |
422 | !priv->at_input_end) |
423 | { |
424 | nread = fill_input_buffer (stream: cstream, at_least_size: count, blocking, cancellable, error); |
425 | if (nread < 0) |
426 | return -1; |
427 | if (nread == 0) |
428 | priv->at_input_end = TRUE; |
429 | } |
430 | |
431 | /* First try to convert any available data (or state) directly to the user buffer: */ |
432 | if (!priv->finished) |
433 | { |
434 | my_error = NULL; |
435 | res = g_converter_convert (converter: priv->converter, |
436 | inbuf: buffer_data (buffer: &priv->input_buffer), |
437 | inbuf_size: buffer_data_size (buffer: &priv->input_buffer), |
438 | outbuf: buffer, outbuf_size: count, |
439 | flags: priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0, |
440 | bytes_read: &bytes_read, |
441 | bytes_written: &bytes_written, |
442 | error: &my_error); |
443 | if (res != G_CONVERTER_ERROR) |
444 | { |
445 | total_bytes_read += bytes_written; |
446 | buffer_consumed (buffer: &priv->input_buffer, count: bytes_read); |
447 | if (res == G_CONVERTER_FINISHED) |
448 | priv->finished = TRUE; /* We're done converting */ |
449 | } |
450 | else if (total_bytes_read == 0 && |
451 | !g_error_matches (error: my_error, |
452 | G_IO_ERROR, |
453 | code: G_IO_ERROR_PARTIAL_INPUT) && |
454 | !g_error_matches (error: my_error, |
455 | G_IO_ERROR, |
456 | code: G_IO_ERROR_NO_SPACE)) |
457 | { |
458 | /* No previously read data and no "special" error, return error */ |
459 | g_propagate_error (dest: error, src: my_error); |
460 | return -1; |
461 | } |
462 | else |
463 | g_error_free (error: my_error); |
464 | } |
465 | |
466 | /* We had some pre-converted data and/or we converted directly to the |
467 | user buffer */ |
468 | if (total_bytes_read > 0) |
469 | return total_bytes_read; |
470 | |
471 | /* If there is no more to convert, return EOF */ |
472 | if (priv->finished) |
473 | { |
474 | g_assert (buffer_data_size (&priv->converted_buffer) == 0); |
475 | return 0; |
476 | } |
477 | |
478 | /* There was "complexity" in the straight-to-buffer conversion, |
479 | * convert to our own buffer and write from that. |
480 | * At this point we didn't produce any data into @buffer. |
481 | */ |
482 | |
483 | /* Ensure we have *some* initial target space */ |
484 | buffer_ensure_space (buffer: &priv->converted_buffer, at_least_size: count); |
485 | |
486 | while (TRUE) |
487 | { |
488 | g_assert (!priv->finished); |
489 | |
490 | /* Try to convert to our buffer */ |
491 | my_error = NULL; |
492 | res = g_converter_convert (converter: priv->converter, |
493 | inbuf: buffer_data (buffer: &priv->input_buffer), |
494 | inbuf_size: buffer_data_size (buffer: &priv->input_buffer), |
495 | outbuf: buffer_data (buffer: &priv->converted_buffer), |
496 | outbuf_size: buffer_tailspace (buffer: &priv->converted_buffer), |
497 | flags: priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0, |
498 | bytes_read: &bytes_read, |
499 | bytes_written: &bytes_written, |
500 | error: &my_error); |
501 | if (res != G_CONVERTER_ERROR) |
502 | { |
503 | priv->converted_buffer.end += bytes_written; |
504 | buffer_consumed (buffer: &priv->input_buffer, count: bytes_read); |
505 | |
506 | /* Maybe we consumed without producing any output */ |
507 | if (buffer_data_size (buffer: &priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED) |
508 | continue; /* Convert more */ |
509 | |
510 | if (res == G_CONVERTER_FINISHED) |
511 | priv->finished = TRUE; |
512 | |
513 | total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer)); |
514 | buffer_read (buffer: &priv->converted_buffer, dest: buffer, count: total_bytes_read); |
515 | |
516 | g_assert (priv->finished || total_bytes_read > 0); |
517 | |
518 | return total_bytes_read; |
519 | } |
520 | |
521 | /* There was some kind of error filling our buffer */ |
522 | |
523 | if (g_error_matches (error: my_error, |
524 | G_IO_ERROR, |
525 | code: G_IO_ERROR_PARTIAL_INPUT) && |
526 | !priv->at_input_end) |
527 | { |
528 | /* Need more data */ |
529 | my_error2 = NULL; |
530 | nread = fill_input_buffer (stream: cstream, |
531 | at_least_size: buffer_data_size (buffer: &priv->input_buffer) + 4096, |
532 | blocking, |
533 | cancellable, |
534 | error: &my_error2); |
535 | if (nread < 0) |
536 | { |
537 | /* Can't read any more data, return that error */ |
538 | g_error_free (error: my_error); |
539 | g_propagate_error (dest: error, src: my_error2); |
540 | priv->need_input = TRUE; |
541 | return -1; |
542 | } |
543 | else if (nread == 0) |
544 | { |
545 | /* End of file, try INPUT_AT_END */ |
546 | priv->at_input_end = TRUE; |
547 | } |
548 | g_error_free (error: my_error); |
549 | continue; |
550 | } |
551 | |
552 | if (g_error_matches (error: my_error, |
553 | G_IO_ERROR, |
554 | code: G_IO_ERROR_NO_SPACE)) |
555 | { |
556 | /* Need more destination space, grow it |
557 | * Note: if we actually grow the buffer (as opposed to compacting it), |
558 | * this will double the size, not just add one byte. */ |
559 | buffer_ensure_space (buffer: &priv->converted_buffer, |
560 | at_least_size: priv->converted_buffer.size + 1); |
561 | g_error_free (error: my_error); |
562 | continue; |
563 | } |
564 | |
565 | /* Any other random error, return it */ |
566 | g_propagate_error (dest: error, src: my_error); |
567 | return -1; |
568 | } |
569 | |
570 | g_assert_not_reached (); |
571 | } |
572 | |
573 | static gssize |
574 | g_converter_input_stream_read (GInputStream *stream, |
575 | void *buffer, |
576 | gsize count, |
577 | GCancellable *cancellable, |
578 | GError **error) |
579 | { |
580 | return read_internal (stream, buffer, count, TRUE, cancellable, error); |
581 | } |
582 | |
583 | static gboolean |
584 | g_converter_input_stream_can_poll (GPollableInputStream *stream) |
585 | { |
586 | GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
587 | |
588 | return (G_IS_POLLABLE_INPUT_STREAM (base_stream) && |
589 | g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream))); |
590 | } |
591 | |
592 | static gboolean |
593 | g_converter_input_stream_is_readable (GPollableInputStream *stream) |
594 | { |
595 | GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
596 | GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream); |
597 | |
598 | if (buffer_data_size (buffer: &cstream->priv->converted_buffer)) |
599 | return TRUE; |
600 | else if (buffer_data_size (buffer: &cstream->priv->input_buffer) && |
601 | !cstream->priv->need_input) |
602 | return TRUE; |
603 | else |
604 | return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream)); |
605 | } |
606 | |
607 | static gssize |
608 | g_converter_input_stream_read_nonblocking (GPollableInputStream *stream, |
609 | void *buffer, |
610 | gsize count, |
611 | GError **error) |
612 | { |
613 | return read_internal (G_INPUT_STREAM (stream), buffer, count, |
614 | FALSE, NULL, error); |
615 | } |
616 | |
617 | static GSource * |
618 | g_converter_input_stream_create_source (GPollableInputStream *stream, |
619 | GCancellable *cancellable) |
620 | { |
621 | GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
622 | GSource *base_source, *pollable_source; |
623 | |
624 | if (g_pollable_input_stream_is_readable (stream)) |
625 | base_source = g_timeout_source_new (interval: 0); |
626 | else |
627 | base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL); |
628 | |
629 | pollable_source = g_pollable_source_new_full (pollable_stream: stream, child_source: base_source, |
630 | cancellable); |
631 | g_source_unref (source: base_source); |
632 | |
633 | return pollable_source; |
634 | } |
635 | |
636 | |
637 | /** |
638 | * g_converter_input_stream_get_converter: |
639 | * @converter_stream: a #GConverterInputStream |
640 | * |
641 | * Gets the #GConverter that is used by @converter_stream. |
642 | * |
643 | * Returns: (transfer none): the converter of the converter input stream |
644 | * |
645 | * Since: 2.24 |
646 | */ |
647 | GConverter * |
648 | g_converter_input_stream_get_converter (GConverterInputStream *converter_stream) |
649 | { |
650 | return converter_stream->priv->converter; |
651 | } |
652 | |