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
27typedef enum {
28 GDK_IO_PIPE_EMPTY,
29 GDK_IO_PIPE_INPUT_BUFFER,
30 GDK_IO_PIPE_OUTPUT_BUFFER
31} GdkIOPipeState;
32
33typedef struct _GdkIOPipe GdkIOPipe;
34
35struct _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
48static GdkIOPipe *
49gdk_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 (mutex: &pipe->mutex);
57 g_cond_init (cond: &pipe->cond);
58
59 return pipe;
60}
61
62static GdkIOPipe *
63gdk_io_pipe_ref (GdkIOPipe *pipe)
64{
65 g_atomic_int_inc (&pipe->ref_count);
66
67 return pipe;
68}
69
70static void
71gdk_io_pipe_unref (GdkIOPipe *pipe)
72{
73 if (!g_atomic_int_dec_and_test (&pipe->ref_count))
74 return;
75
76 g_cond_clear (cond: &pipe->cond);
77 g_mutex_clear (mutex: &pipe->mutex);
78}
79
80static void
81gdk_io_pipe_lock (GdkIOPipe *pipe)
82{
83 g_mutex_lock (mutex: &pipe->mutex);
84}
85
86static void
87gdk_io_pipe_unlock (GdkIOPipe *pipe)
88{
89 g_mutex_unlock (mutex: &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
101typedef struct _GdkPipeInputStream GdkPipeInputStream;
102typedef struct _GdkPipeInputStreamClass GdkPipeInputStreamClass;
103
104struct _GdkPipeInputStream
105{
106 GInputStream parent;
107
108 GdkIOPipe *pipe;
109};
110
111struct _GdkPipeInputStreamClass
112{
113 GInputStreamClass parent_class;
114};
115
116GType gdk_pipe_input_stream_get_type (void) G_GNUC_CONST;
117
118G_DEFINE_TYPE (GdkPipeInputStream, gdk_pipe_input_stream, G_TYPE_INPUT_STREAM)
119
120static void
121gdk_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
130static gssize
131gdk_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 (cond: &pipe->cond, mutex: &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 (dest: buffer, src: pipe->buffer, n: 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 (cond: &pipe->cond);
191 gdk_io_pipe_unlock (pipe);
192
193 return amount;
194}
195
196static gboolean
197gdk_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 (cond: &pipe->cond);
208
209 gdk_io_pipe_unlock (pipe);
210
211 return TRUE;
212}
213
214static void
215gdk_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
226static void
227gdk_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
240typedef struct _GdkPipeOutputStream GdkPipeOutputStream;
241typedef struct _GdkPipeOutputStreamClass GdkPipeOutputStreamClass;
242
243struct _GdkPipeOutputStream
244{
245 GOutputStream parent;
246
247 GdkIOPipe *pipe;
248};
249
250struct _GdkPipeOutputStreamClass
251{
252 GOutputStreamClass parent_class;
253};
254
255GType gdk_pipe_output_stream_get_type (void) G_GNUC_CONST;
256
257G_DEFINE_TYPE (GdkPipeOutputStream, gdk_pipe_output_stream, G_TYPE_OUTPUT_STREAM)
258
259static void
260gdk_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
269static gssize
270gdk_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 (cond: &pipe->cond, mutex: &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 (dest: pipe->buffer, src: buffer, n: 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 (cond: &pipe->cond);
326 gdk_io_pipe_unlock (pipe);
327
328 return amount;
329}
330
331static gboolean
332gdk_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 (cond: &pipe->cond);
344 gdk_io_pipe_unlock (pipe);
345
346 return TRUE;
347}
348
349static void
350gdk_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
361static void
362gdk_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
375typedef struct _GdkPipeIOStream GdkPipeIOStream;
376typedef struct _GdkPipeIOStreamClass GdkPipeIOStreamClass;
377
378struct _GdkPipeIOStream
379{
380 GIOStream parent;
381
382 GInputStream *input_stream;
383 GOutputStream *output_stream;
384 GdkIOPipe *pipe;
385};
386
387struct _GdkPipeIOStreamClass
388{
389 GIOStreamClass parent_class;
390};
391
392GType gdk_pipe_io_stream_get_type (void) G_GNUC_CONST;
393
394G_DEFINE_TYPE (GdkPipeIOStream, gdk_pipe_io_stream, G_TYPE_IO_STREAM)
395
396static void
397gdk_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
408static GInputStream *
409gdk_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
416static GOutputStream *
417gdk_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
424static gboolean
425gdk_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
433static void
434gdk_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
446static void
447gdk_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->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->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 */
471GIOStream *
472gdk_pipe_io_stream_new (void)
473{
474 return g_object_new (GDK_TYPE_PIPE_IO_STREAM, NULL);
475}
476

source code of gtk/gdk/gdkpipeiostream.c