1 /* GDK - The GIMP Drawing Kit
2  *
3  * Copyright (C) 2017 Benjamin Otte <otte@gnome.org>
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 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 Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include "gdkpipeiostreamprivate.h"
22 
23 #include <string.h>
24 
25 /* PIPE */
26 
27 typedef enum {
28   GDK_IO_PIPE_EMPTY,
29   GDK_IO_PIPE_INPUT_BUFFER,
30   GDK_IO_PIPE_OUTPUT_BUFFER
31 } GdkIOPipeState;
32 
33 typedef struct _GdkIOPipe GdkIOPipe;
34 
35 struct _GdkIOPipe
36 {
37   int ref_count;
38 
39   GMutex mutex;
40   GCond cond;
41   guchar *buffer;
42   gsize size;
43   GdkIOPipeState state : 2;
44   guint input_closed : 1;
45   guint output_closed : 1;
46 };
47 
48 static GdkIOPipe *
gdk_io_pipe_new(void)49 gdk_io_pipe_new (void)
50 {
51   GdkIOPipe *pipe;
52 
53   pipe = g_slice_new0 (GdkIOPipe);
54   pipe->ref_count = 1;
55 
56   g_mutex_init (&pipe->mutex);
57   g_cond_init (&pipe->cond);
58 
59   return pipe;
60 }
61 
62 static GdkIOPipe *
gdk_io_pipe_ref(GdkIOPipe * pipe)63 gdk_io_pipe_ref (GdkIOPipe *pipe)
64 {
65   g_atomic_int_inc (&pipe->ref_count);
66 
67   return pipe;
68 }
69 
70 static void
gdk_io_pipe_unref(GdkIOPipe * pipe)71 gdk_io_pipe_unref (GdkIOPipe *pipe)
72 {
73   if (!g_atomic_int_dec_and_test (&pipe->ref_count))
74     return;
75 
76   g_cond_clear (&pipe->cond);
77   g_mutex_clear (&pipe->mutex);
78 }
79 
80 static void
gdk_io_pipe_lock(GdkIOPipe * pipe)81 gdk_io_pipe_lock (GdkIOPipe *pipe)
82 {
83   g_mutex_lock (&pipe->mutex);
84 }
85 
86 static void
gdk_io_pipe_unlock(GdkIOPipe * pipe)87 gdk_io_pipe_unlock (GdkIOPipe *pipe)
88 {
89   g_mutex_unlock (&pipe->mutex);
90 }
91 
92 /* INPUT STREAM */
93 
94 #define GDK_TYPE_PIPE_INPUT_STREAM            (gdk_pipe_input_stream_get_type ())
95 #define GDK_PIPE_INPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStream))
96 #define GDK_IS_PIPE_INPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_INPUT_STREAM))
97 #define GDK_PIPE_INPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStreamClass))
98 #define GDK_IS_PIPE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_INPUT_STREAM))
99 #define GDK_PIPE_INPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStreamClass))
100 
101 typedef struct _GdkPipeInputStream GdkPipeInputStream;
102 typedef struct _GdkPipeInputStreamClass GdkPipeInputStreamClass;
103 
104 struct _GdkPipeInputStream
105 {
106   GInputStream parent;
107 
108   GdkIOPipe *pipe;
109 };
110 
111 struct _GdkPipeInputStreamClass
112 {
113   GInputStreamClass parent_class;
114 };
115 
116 GType gdk_pipe_input_stream_get_type (void) G_GNUC_CONST;
117 
G_DEFINE_TYPE(GdkPipeInputStream,gdk_pipe_input_stream,G_TYPE_INPUT_STREAM)118 G_DEFINE_TYPE (GdkPipeInputStream, gdk_pipe_input_stream, G_TYPE_INPUT_STREAM)
119 
120 static void
121 gdk_pipe_input_stream_finalize (GObject *object)
122 {
123   GdkPipeInputStream *pipe = GDK_PIPE_INPUT_STREAM (object);
124 
125   g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref);
126 
127   G_OBJECT_CLASS (gdk_pipe_input_stream_parent_class)->finalize (object);
128 }
129 
130 static gssize
gdk_pipe_input_stream_read(GInputStream * stream,void * buffer,gsize count,GCancellable * cancellable,GError ** error)131 gdk_pipe_input_stream_read (GInputStream  *stream,
132                             void          *buffer,
133                             gsize          count,
134                             GCancellable  *cancellable,
135                             GError       **error)
136 {
137   GdkPipeInputStream *pipe_stream = GDK_PIPE_INPUT_STREAM (stream);
138   GdkIOPipe *pipe = pipe_stream->pipe;
139   gsize amount;
140 
141   gdk_io_pipe_lock (pipe);
142 
143   switch (pipe->state)
144   {
145     case GDK_IO_PIPE_EMPTY:
146       if (pipe->output_closed)
147         {
148           amount = 0;
149           break;
150         }
151       pipe->buffer = buffer;
152       pipe->size = count;
153       pipe->state = GDK_IO_PIPE_INPUT_BUFFER;
154       do
155         g_cond_wait (&pipe->cond, &pipe->mutex);
156       while (pipe->size == count &&
157              pipe->state == GDK_IO_PIPE_INPUT_BUFFER &&
158              !pipe->output_closed);
159       if (pipe->state == GDK_IO_PIPE_INPUT_BUFFER)
160         {
161           amount = count - pipe->size;
162           pipe->state = GDK_IO_PIPE_EMPTY;
163           pipe->size = 0;
164         }
165       else
166         {
167           amount = count;
168         }
169       break;
170 
171     case GDK_IO_PIPE_OUTPUT_BUFFER:
172       amount = MIN (count, pipe->size);
173 
174       memcpy (buffer, pipe->buffer, amount);
175       pipe->size -= amount;
176 
177       if (pipe->size == 0)
178         pipe->state = GDK_IO_PIPE_EMPTY;
179       else
180         pipe->buffer += amount;
181       break;
182 
183     case GDK_IO_PIPE_INPUT_BUFFER:
184     default:
185       g_assert_not_reached ();
186       amount = 0;
187       break;
188   }
189 
190   g_cond_broadcast (&pipe->cond);
191   gdk_io_pipe_unlock (pipe);
192 
193   return amount;
194 }
195 
196 static gboolean
gdk_pipe_input_stream_close(GInputStream * stream,GCancellable * cancellable,GError ** error)197 gdk_pipe_input_stream_close (GInputStream  *stream,
198                              GCancellable  *cancellable,
199                              GError       **error)
200 {
201   GdkPipeInputStream *pipe_stream = GDK_PIPE_INPUT_STREAM (stream);
202   GdkIOPipe *pipe = pipe_stream->pipe;
203 
204   gdk_io_pipe_lock (pipe);
205 
206   pipe->input_closed = TRUE;
207   g_cond_broadcast (&pipe->cond);
208 
209   gdk_io_pipe_unlock (pipe);
210 
211   return TRUE;
212 }
213 
214 static void
gdk_pipe_input_stream_class_init(GdkPipeInputStreamClass * class)215 gdk_pipe_input_stream_class_init (GdkPipeInputStreamClass *class)
216 {
217   GObjectClass *object_class = G_OBJECT_CLASS (class);
218   GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (class);
219 
220   object_class->finalize = gdk_pipe_input_stream_finalize;
221 
222   input_stream_class->read_fn = gdk_pipe_input_stream_read;
223   input_stream_class->close_fn = gdk_pipe_input_stream_close;
224 }
225 
226 static void
gdk_pipe_input_stream_init(GdkPipeInputStream * pipe)227 gdk_pipe_input_stream_init (GdkPipeInputStream *pipe)
228 {
229 }
230 
231 /* OUTPUT STREAM */
232 
233 #define GDK_TYPE_PIPE_OUTPUT_STREAM            (gdk_pipe_output_stream_get_type ())
234 #define GDK_PIPE_OUTPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStream))
235 #define GDK_IS_PIPE_OUTPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM))
236 #define GDK_PIPE_OUTPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStreamClass))
237 #define GDK_IS_PIPE_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_OUTPUT_STREAM))
238 #define GDK_PIPE_OUTPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStreamClass))
239 
240 typedef struct _GdkPipeOutputStream GdkPipeOutputStream;
241 typedef struct _GdkPipeOutputStreamClass GdkPipeOutputStreamClass;
242 
243 struct _GdkPipeOutputStream
244 {
245   GOutputStream parent;
246 
247   GdkIOPipe *pipe;
248 };
249 
250 struct _GdkPipeOutputStreamClass
251 {
252   GOutputStreamClass parent_class;
253 };
254 
255 GType gdk_pipe_output_stream_get_type (void) G_GNUC_CONST;
256 
G_DEFINE_TYPE(GdkPipeOutputStream,gdk_pipe_output_stream,G_TYPE_OUTPUT_STREAM)257 G_DEFINE_TYPE (GdkPipeOutputStream, gdk_pipe_output_stream, G_TYPE_OUTPUT_STREAM)
258 
259 static void
260 gdk_pipe_output_stream_finalize (GObject *object)
261 {
262   GdkPipeOutputStream *pipe = GDK_PIPE_OUTPUT_STREAM (object);
263 
264   g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref);
265 
266   G_OBJECT_CLASS (gdk_pipe_output_stream_parent_class)->finalize (object);
267 }
268 
269 static gssize
gdk_pipe_output_stream_write(GOutputStream * stream,const void * buffer,gsize count,GCancellable * cancellable,GError ** error)270 gdk_pipe_output_stream_write (GOutputStream  *stream,
271                               const void     *buffer,
272                               gsize           count,
273                               GCancellable   *cancellable,
274                               GError        **error)
275 {
276   GdkPipeOutputStream *pipe_stream = GDK_PIPE_OUTPUT_STREAM (stream);
277   GdkIOPipe *pipe = pipe_stream->pipe;
278   gsize amount;
279 
280   gdk_io_pipe_lock (pipe);
281 
282   switch (pipe->state)
283   {
284     case GDK_IO_PIPE_EMPTY:
285       pipe->buffer = (void *) buffer;
286       pipe->size = count;
287       pipe->state = GDK_IO_PIPE_OUTPUT_BUFFER;
288       while (pipe->size == count &&
289              pipe->state == GDK_IO_PIPE_OUTPUT_BUFFER &&
290              !pipe->input_closed)
291         g_cond_wait (&pipe->cond, &pipe->mutex);
292       if (pipe->state == GDK_IO_PIPE_OUTPUT_BUFFER)
293         {
294           amount = count - pipe->size;
295           pipe->state = GDK_IO_PIPE_EMPTY;
296           pipe->size = 0;
297           if (pipe->input_closed && amount == 0)
298             amount = count;
299         }
300       else
301         {
302           amount = count;
303         }
304       break;
305 
306     case GDK_IO_PIPE_INPUT_BUFFER:
307       amount = MIN (count, pipe->size);
308 
309       memcpy (pipe->buffer, buffer, amount);
310       pipe->size -= amount;
311 
312       if (pipe->size == 0)
313         pipe->state = GDK_IO_PIPE_EMPTY;
314       else
315         pipe->buffer += amount;
316       break;
317 
318     case GDK_IO_PIPE_OUTPUT_BUFFER:
319     default:
320       g_assert_not_reached ();
321       amount = 0;
322       break;
323   }
324 
325   g_cond_broadcast (&pipe->cond);
326   gdk_io_pipe_unlock (pipe);
327 
328   return amount;
329 }
330 
331 static gboolean
gdk_pipe_output_stream_close(GOutputStream * stream,GCancellable * cancellable,GError ** error)332 gdk_pipe_output_stream_close (GOutputStream  *stream,
333                               GCancellable  *cancellable,
334                               GError       **error)
335 {
336   GdkPipeOutputStream *pipe_stream = GDK_PIPE_OUTPUT_STREAM (stream);
337   GdkIOPipe *pipe = pipe_stream->pipe;
338 
339   gdk_io_pipe_lock (pipe);
340 
341   pipe->output_closed = TRUE;
342 
343   g_cond_broadcast (&pipe->cond);
344   gdk_io_pipe_unlock (pipe);
345 
346   return TRUE;
347 }
348 
349 static void
gdk_pipe_output_stream_class_init(GdkPipeOutputStreamClass * class)350 gdk_pipe_output_stream_class_init (GdkPipeOutputStreamClass *class)
351 {
352   GObjectClass *object_class = G_OBJECT_CLASS (class);
353   GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (class);
354 
355   object_class->finalize = gdk_pipe_output_stream_finalize;
356 
357   output_stream_class->write_fn = gdk_pipe_output_stream_write;
358   output_stream_class->close_fn = gdk_pipe_output_stream_close;
359 }
360 
361 static void
gdk_pipe_output_stream_init(GdkPipeOutputStream * pipe)362 gdk_pipe_output_stream_init (GdkPipeOutputStream *pipe)
363 {
364 }
365 
366 /* IOSTREAM */
367 
368 #define GDK_TYPE_PIPE_IO_STREAM            (gdk_pipe_io_stream_get_type ())
369 #define GDK_PIPE_IO_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStream))
370 #define GDK_IS_PIPE_IO_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_IO_STREAM))
371 #define GDK_PIPE_IO_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStreamClass))
372 #define GDK_IS_PIPE_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_IO_STREAM))
373 #define GDK_PIPE_IO_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStreamClass))
374 
375 typedef struct _GdkPipeIOStream GdkPipeIOStream;
376 typedef struct _GdkPipeIOStreamClass GdkPipeIOStreamClass;
377 
378 struct _GdkPipeIOStream
379 {
380   GIOStream parent;
381 
382   GInputStream *input_stream;
383   GOutputStream *output_stream;
384   GdkIOPipe *pipe;
385 };
386 
387 struct _GdkPipeIOStreamClass
388 {
389   GIOStreamClass parent_class;
390 };
391 
392 GType gdk_pipe_io_stream_get_type (void) G_GNUC_CONST;
393 
G_DEFINE_TYPE(GdkPipeIOStream,gdk_pipe_io_stream,G_TYPE_IO_STREAM)394 G_DEFINE_TYPE (GdkPipeIOStream, gdk_pipe_io_stream, G_TYPE_IO_STREAM)
395 
396 static void
397 gdk_pipe_io_stream_finalize (GObject *object)
398 {
399   GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (object);
400 
401   g_clear_object (&pipe->input_stream);
402   g_clear_object (&pipe->output_stream);
403   g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref);
404 
405   G_OBJECT_CLASS (gdk_pipe_io_stream_parent_class)->finalize (object);
406 }
407 
408 static GInputStream *
gdk_pipe_io_stream_get_input_stream(GIOStream * stream)409 gdk_pipe_io_stream_get_input_stream (GIOStream *stream)
410 {
411   GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (stream);
412 
413   return pipe->input_stream;
414 }
415 
416 static GOutputStream *
gdk_pipe_io_stream_get_output_stream(GIOStream * stream)417 gdk_pipe_io_stream_get_output_stream (GIOStream *stream)
418 {
419   GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (stream);
420 
421   return pipe->output_stream;
422 }
423 
424 static gboolean
gdk_pipe_io_stream_close(GIOStream * stream,GCancellable * cancellable,GError ** error)425 gdk_pipe_io_stream_close (GIOStream     *stream,
426                           GCancellable  *cancellable,
427                           GError       **error)
428 {
429   /* overwrite so we don't close the 2 streams */
430   return TRUE;
431 }
432 
433 static void
gdk_pipe_io_stream_class_init(GdkPipeIOStreamClass * class)434 gdk_pipe_io_stream_class_init (GdkPipeIOStreamClass *class)
435 {
436   GObjectClass *object_class = G_OBJECT_CLASS (class);
437   GIOStreamClass *io_class = G_IO_STREAM_CLASS (class);
438 
439   object_class->finalize = gdk_pipe_io_stream_finalize;
440 
441   io_class->get_input_stream = gdk_pipe_io_stream_get_input_stream;
442   io_class->get_output_stream = gdk_pipe_io_stream_get_output_stream;
443   io_class->close_fn = gdk_pipe_io_stream_close;
444 }
445 
446 static void
gdk_pipe_io_stream_init(GdkPipeIOStream * pipe)447 gdk_pipe_io_stream_init (GdkPipeIOStream *pipe)
448 {
449   pipe->pipe = gdk_io_pipe_new ();
450 
451   pipe->input_stream = g_object_new (GDK_TYPE_PIPE_INPUT_STREAM, NULL);
452   GDK_PIPE_INPUT_STREAM (pipe->input_stream)->pipe = gdk_io_pipe_ref (pipe->pipe);
453 
454   pipe->output_stream = g_object_new (GDK_TYPE_PIPE_OUTPUT_STREAM, NULL);
455   GDK_PIPE_OUTPUT_STREAM (pipe->output_stream)->pipe = gdk_io_pipe_ref (pipe->pipe);
456 }
457 
458 /**
459  * gdk_pipe_io_stream_new:
460  *
461  * Creates a `GIOStream` whose input- and output-stream behave like a pipe.
462  *
463  * Data written into the output stream becomes available for reading on
464  * the input stream.
465  *
466  * Note that this is data transfer in the opposite direction to
467  * g_output_stream_splice().
468  *
469  * Returns: a new `GIOStream`
470  */
471 GIOStream *
gdk_pipe_io_stream_new(void)472 gdk_pipe_io_stream_new (void)
473 {
474   return g_object_new (GDK_TYPE_PIPE_IO_STREAM, NULL);
475 }
476