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