summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-11-25 01:27:33 -0200
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-11-25 17:27:32 -0200
commit46341b329d72736c1f1c47478f760ab8db76bbc8 (patch)
treef659749812561bd8aa1bb5e6ae386977ed6fe797
parent16be61c7e15d89ba9ba4529e0d280dcb5fb81da7 (diff)
efl_io_buffered_stream: wraps an I/O object and make it easy to use.
Since all other efl.io objects are low-level, the recommended approach is to use an efl.io.copier. However when dealing with in-memory, bi-directional comms like talking to a socket, we always end with 2 queues, 2 copiers and the annoying setup that is being replicated in ecore_ipc, efl_debug and so on. This class is the base to make it simpler. Other classes such as Efl.Net.Socket.Simple, Efl.Net.Dialer.Simple and Efl.Net.Server.Simple will use it to provide simpler code to users. I guess we can call EFL+EO Java now?
-rw-r--r--src/Makefile_Ecore.am5
-rw-r--r--src/examples/ecore/.gitignore1
-rw-r--r--src/examples/ecore/Makefile.am5
-rw-r--r--src/examples/ecore/efl_io_buffered_stream_example.c312
-rw-r--r--src/lib/ecore/Ecore_Eo.h1
-rw-r--r--src/lib/ecore/efl_io_buffered_stream.c549
-rw-r--r--src/lib/ecore/efl_io_buffered_stream.eo251
7 files changed, 1123 insertions, 1 deletions
diff --git a/src/Makefile_Ecore.am b/src/Makefile_Ecore.am
index 1eeab08..7ebef69 100644
--- a/src/Makefile_Ecore.am
+++ b/src/Makefile_Ecore.am
@@ -19,7 +19,9 @@ ecore_eolian_files_public = \
19 lib/ecore/efl_io_stdout.eo \ 19 lib/ecore/efl_io_stdout.eo \
20 lib/ecore/efl_io_stderr.eo \ 20 lib/ecore/efl_io_stderr.eo \
21 lib/ecore/efl_io_file.eo \ 21 lib/ecore/efl_io_file.eo \
22 lib/ecore/efl_io_copier.eo 22 lib/ecore/efl_io_copier.eo \
23 lib/ecore/efl_io_buffered_stream.eo
24
23 25
24ecore_eolian_files = \ 26ecore_eolian_files = \
25 $(ecore_eolian_files_public) \ 27 $(ecore_eolian_files_public) \
@@ -82,6 +84,7 @@ lib/ecore/efl_io_stdout.c \
82lib/ecore/efl_io_stderr.c \ 84lib/ecore/efl_io_stderr.c \
83lib/ecore/efl_io_file.c \ 85lib/ecore/efl_io_file.c \
84lib/ecore/efl_io_copier.c \ 86lib/ecore/efl_io_copier.c \
87lib/ecore/efl_io_buffered_stream.c \
85lib/ecore/efl_promise.c \ 88lib/ecore/efl_promise.c \
86lib/ecore/ecore_pipe.c \ 89lib/ecore/ecore_pipe.c \
87lib/ecore/ecore_poller.c \ 90lib/ecore/ecore_poller.c \
diff --git a/src/examples/ecore/.gitignore b/src/examples/ecore/.gitignore
index e8e8aa9..b353fa7 100644
--- a/src/examples/ecore/.gitignore
+++ b/src/examples/ecore/.gitignore
@@ -51,6 +51,7 @@
51/efl_io_copier_example 51/efl_io_copier_example
52/efl_io_copier_simple_example 52/efl_io_copier_simple_example
53/efl_io_queue_example 53/efl_io_queue_example
54/efl_io_buffered_stream_example
54/efl_net_server_example 55/efl_net_server_example
55/efl_net_dialer_http_example 56/efl_net_dialer_http_example
56/efl_net_dialer_websocket_example 57/efl_net_dialer_websocket_example
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index bd5796a..db23d30 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -84,6 +84,7 @@ ecore_con_eet_server_example \
84efl_io_copier_example \ 84efl_io_copier_example \
85efl_io_copier_simple_example \ 85efl_io_copier_simple_example \
86efl_io_queue_example \ 86efl_io_queue_example \
87efl_io_buffered_stream_example \
87efl_net_server_example \ 88efl_net_server_example \
88efl_net_dialer_http_example \ 89efl_net_dialer_http_example \
89efl_net_dialer_websocket_example \ 90efl_net_dialer_websocket_example \
@@ -317,6 +318,9 @@ efl_io_copier_simple_example_LDADD = $(ECORE_COMMON_LDADD)
317efl_io_queue_example_SOURCES = efl_io_queue_example.c 318efl_io_queue_example_SOURCES = efl_io_queue_example.c
318efl_io_queue_example_LDADD = $(ECORE_CON_COMMON_LDADD) 319efl_io_queue_example_LDADD = $(ECORE_CON_COMMON_LDADD)
319 320
321efl_io_buffered_stream_example_SOURCES = efl_io_buffered_stream_example.c
322efl_io_buffered_stream_example_LDADD = $(ECORE_CON_COMMON_LDADD)
323
320efl_net_server_example_SOURCES = efl_net_server_example.c 324efl_net_server_example_SOURCES = efl_net_server_example.c
321efl_net_server_example_LDADD = $(ECORE_CON_COMMON_LDADD) 325efl_net_server_example_LDADD = $(ECORE_CON_COMMON_LDADD)
322 326
@@ -407,6 +411,7 @@ ecore_con_eet_descriptor_example.c \
407efl_io_copier_example.c \ 411efl_io_copier_example.c \
408efl_io_copier_simple_example.c \ 412efl_io_copier_simple_example.c \
409efl_io_queue_example.c \ 413efl_io_queue_example.c \
414efl_io_buffered_stream_example.c \
410efl_net_server_example.c \ 415efl_net_server_example.c \
411efl_net_dialer_http_example.c \ 416efl_net_dialer_http_example.c \
412efl_net_dialer_websocket_example.c \ 417efl_net_dialer_websocket_example.c \
diff --git a/src/examples/ecore/efl_io_buffered_stream_example.c b/src/examples/ecore/efl_io_buffered_stream_example.c
new file mode 100644
index 0000000..ec8ff1f
--- /dev/null
+++ b/src/examples/ecore/efl_io_buffered_stream_example.c
@@ -0,0 +1,312 @@
1#define EFL_BETA_API_SUPPORT 1
2#define EFL_EO_API_SUPPORT 1
3#include <Ecore.h>
4#include <Ecore_Getopt.h>
5#include <Ecore_Con.h>
6
7static int retval = EXIT_SUCCESS;
8static Eina_List *commands = NULL;
9static Eina_Slice line_delimiter;
10static Eo *stream = NULL;
11
12static void
13_command_next(void)
14{
15 Eina_Slice slice;
16 char *cmd;
17
18 if (!commands)
19 {
20 efl_io_buffered_stream_eos_mark(stream);
21 return;
22 }
23
24 cmd = commands->data;
25 commands = eina_list_remove_list(commands, commands);
26
27 slice = (Eina_Slice)EINA_SLICE_STR(cmd);
28 efl_io_writer_write(stream, &slice, NULL);
29 fprintf(stderr, "INFO: sent '" EINA_SLICE_STR_FMT "'\n",
30 EINA_SLICE_STR_PRINT(slice));
31
32 /* don't use line_delimiter directly, 'len' may be changed! */
33 slice = line_delimiter;
34 efl_io_writer_write(stream, &slice, NULL);
35 free(cmd);
36}
37
38static void
39_receiver_data(void *data EINA_UNUSED, const Efl_Event *event)
40{
41 Eina_Slice slice;
42
43 if (!efl_io_buffered_stream_slice_get(event->object, &slice)) return;
44
45 /* this will happen when we're called when we issue our own
46 * efl_io_buffered_stream_clear() below.
47 */
48 if (slice.len == 0) return;
49
50 /*
51 * If the server didn't send us the line terminator and closed the
52 * connection (ie: efl_io_reader_eos_get() == true) or if the
53 * efl_io_buffered_stream_max_queue_size_input_set() was reached,
54 * then we may have a line without a trailing delimiter. Check for
55 * that.
56 */
57 if (!eina_slice_endswith(slice, line_delimiter))
58 {
59 fprintf(stderr, "WARNING: received without line-delimiter '"
60 EINA_SLICE_STR_FMT "'\n",
61 EINA_SLICE_STR_PRINT(slice));
62 }
63 else
64 {
65 slice.len -= line_delimiter.len;
66 fprintf(stderr, "INFO: received '" EINA_SLICE_STR_FMT "'\n",
67 EINA_SLICE_STR_PRINT(slice));
68 }
69
70 efl_io_buffered_stream_clear(event->object);
71 _command_next();
72}
73
74static void
75_dialer_connected(void *data EINA_UNUSED, const Efl_Event *event)
76{
77 fprintf(stderr, "INFO: connected to %s (%s)\n",
78 efl_net_dialer_address_dial_get(event->object),
79 efl_net_socket_address_remote_get(event->object));
80
81 _command_next();
82}
83
84static void
85_stream_write_finished(void *data EINA_UNUSED, const Efl_Event *event)
86{
87 fprintf(stderr, "INFO: %s done sending\n", efl_name_get(event->object));
88}
89
90static void
91_stream_error(void *data EINA_UNUSED, const Efl_Event *event)
92{
93 const Eina_Error *perr = event->info;
94 fprintf(stderr, "INFO: %s error: #%d '%s'\n",
95 efl_name_get(event->object), *perr, eina_error_msg_get(*perr));
96 retval = EXIT_FAILURE;
97 ecore_main_loop_quit();
98}
99
100static void
101_stream_eos(void *data EINA_UNUSED, const Efl_Event *event)
102{
103 fprintf(stderr, "INFO: %s eos, quit\n", efl_name_get(event->object));
104 ecore_main_loop_quit();
105}
106
107EFL_CALLBACKS_ARRAY_DEFINE(stream_cbs,
108 { EFL_IO_BUFFERED_STREAM_EVENT_LINE, _receiver_data },
109 { EFL_IO_READER_EVENT_EOS, _stream_eos },
110 { EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _stream_write_finished },
111 { EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _stream_error });
112
113static char *
114_unescape(const char *str)
115{
116 char *ret = strdup(str);
117 char *c, *w;
118 Eina_Bool escaped = EINA_FALSE;
119
120 for (c = ret, w = ret; *c != '\0'; c++)
121 {
122 if (escaped)
123 {
124 escaped = EINA_FALSE;
125 switch (*c)
126 {
127 case 'n': *w = '\n'; break;
128 case 'r': *w = '\r'; break;
129 case 't': *w = '\t'; break;
130 default: w++; /* no change */
131 }
132 w++;
133 }
134 else
135 {
136 if (*c == '\\')
137 escaped = EINA_TRUE;
138 else
139 w++;
140 }
141 }
142 *w = '\0';
143 return ret;
144}
145
146static const Ecore_Getopt options = {
147 "efl_io_buffered_stream_example", /* program name */
148 NULL, /* usage line */
149 "1", /* version */
150 "(C) 2016 Enlightenment Project", /* copyright */
151 "BSD 2-Clause", /* license */
152 /* long description, may be multiline and contain \n */
153 "Example of Efl_Io_Buffered_Stream usage.\n"
154 "\n"
155 "This uses Efl_Io_Buffered_Stream to easily interface with Efl_Net_Dialer_Tcp.",
156 EINA_FALSE,
157 {
158 ECORE_GETOPT_STORE_STR('d', "line-delimiter",
159 "Changes the line delimiter to be used in both send and receive. Defaults to \\r\\n"),
160 ECORE_GETOPT_STORE_ULONG('l', "buffer-limit",
161 "If set will limit buffer size to this limit of bytes. If used alongside with --line-delimiter and that delimiter was not found but bffer limit was reached, the line event will be triggered without the delimiter at the end."),
162 ECORE_GETOPT_VERSION('V', "version"),
163 ECORE_GETOPT_COPYRIGHT('C', "copyright"),
164 ECORE_GETOPT_LICENSE('L', "license"),
165 ECORE_GETOPT_HELP('h', "help"),
166
167 ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
168 "The server address as\n"
169 "IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n",
170 "server_address"),
171 ECORE_GETOPT_APPEND_METAVAR(0, NULL,
172 "Commands to send",
173 "commands",
174 ECORE_GETOPT_TYPE_STR),
175 ECORE_GETOPT_SENTINEL
176 }
177};
178
179int
180main(int argc, char **argv)
181{
182 char *address = NULL;
183 char *line_delimiter_str = NULL;
184 char *cmd;
185 unsigned long buffer_limit = 0;
186 Eina_Bool quit_option = EINA_FALSE;
187 Ecore_Getopt_Value values[] = {
188 ECORE_GETOPT_VALUE_STR(line_delimiter_str),
189 ECORE_GETOPT_VALUE_ULONG(buffer_limit),
190
191 /* standard block to provide version, copyright, license and help */
192 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
193 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
194 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
195 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
196
197 /* positional argument */
198 ECORE_GETOPT_VALUE_STR(address),
199 ECORE_GETOPT_VALUE_LIST(commands),
200
201 ECORE_GETOPT_VALUE_NONE /* sentinel */
202 };
203 Eina_Error err;
204 int args;
205 Eo *dialer, *loop;
206
207 ecore_init();
208 ecore_con_init();
209
210 args = ecore_getopt_parse(&options, values, argc, argv);
211 if (args < 0)
212 {
213 fputs("ERROR: Could not parse command line options.\n", stderr);
214 retval = EXIT_FAILURE;
215 goto end;
216 }
217
218 if (quit_option) goto end;
219
220 args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
221 if (args < 0)
222 {
223 fputs("ERROR: Could not parse positional arguments.\n", stderr);
224 retval = EXIT_FAILURE;
225 goto end;
226 }
227
228 line_delimiter_str = _unescape(line_delimiter_str ? line_delimiter_str : "\\r\\n");
229
230 if (!commands)
231 {
232 fputs("ERROR: missing commands to send.\n", stderr);
233 retval = EXIT_FAILURE;
234 goto end;
235 }
236
237 /*
238 * some objects such as the Efl.Io.Buffered_Stream and
239 * Efl.Net.Dialer.Tcp depend on main loop, thus their parent must
240 * be a loop provider. We use the loop itself.
241 */
242 loop = ecore_main_loop_get();
243
244 /* The TCP client to use to send/receive network data */
245 dialer = efl_add(EFL_NET_DIALER_TCP_CLASS, loop,
246 efl_name_set(efl_added, "dialer"),
247 efl_event_callback_add(efl_added, EFL_NET_DIALER_EVENT_CONNECTED, _dialer_connected, NULL));
248 if (!dialer)
249 {
250 fprintf(stderr, "ERROR: could not create Efl_Net_Dialer_Tcp\n");
251 retval = EXIT_FAILURE;
252 goto end;
253 }
254
255 line_delimiter = (Eina_Slice)EINA_SLICE_STR(line_delimiter_str);
256
257 /*
258 * Without the buffered stream we'd have to create two Efl.Io.Queue
259 * ourselves, as well as two Efl.Io.Copier to link them with the
260 * dialer.
261 *
262 * Our example's usage is to write each command at once followed by
263 * the line_delimiter, then wait for a reply from the server, then
264 * write another.
265 *
266 * On incoming data we peek at it with slice_get() and then clear().
267 */
268 stream = efl_add(EFL_IO_BUFFERED_STREAM_CLASS, loop,
269 efl_name_set(efl_added, "stream"),
270 efl_io_buffered_stream_inner_io_set(efl_added, dialer), /* mandatory! */
271 efl_io_buffered_stream_line_delimiter_set(efl_added, &line_delimiter),
272 efl_io_buffered_stream_max_queue_size_input_set(efl_added, buffer_limit),
273 efl_io_buffered_stream_max_queue_size_output_set(efl_added, buffer_limit),
274 efl_event_callback_array_add(efl_added, stream_cbs(), NULL));
275 if (!stream)
276 {
277 fprintf(stderr, "ERROR: could not create Efl_Io_Buffered_Stream\n");
278 retval = EXIT_FAILURE;
279 goto error_stream;
280 }
281
282 /*
283 * From here on it's mostly the same all Efl_Io_Copier would do,
284 * check efl_io_copier_simple_example.c and efl_io_copier_example.c
285 */
286 err = efl_net_dialer_dial(dialer, address);
287 if (err)
288 {
289 fprintf(stderr, "ERROR: could not dial %s: %s\n",
290 address, eina_error_msg_get(err));
291 goto error_dialing;
292 }
293
294 ecore_main_loop_begin();
295
296 error_dialing:
297 efl_io_closer_close(stream);
298 efl_del(stream);
299 error_stream:
300 efl_del(dialer);
301 end:
302 EINA_LIST_FREE(commands, cmd)
303 {
304 fprintf(stderr, "ERROR: unsent command: %s\n", cmd);
305 free(cmd);
306 }
307
308 ecore_con_shutdown();
309 ecore_shutdown();
310
311 return retval;
312}
diff --git a/src/lib/ecore/Ecore_Eo.h b/src/lib/ecore/Ecore_Eo.h
index fc46941..1387afb 100644
--- a/src/lib/ecore/Ecore_Eo.h
+++ b/src/lib/ecore/Ecore_Eo.h
@@ -110,6 +110,7 @@ EAPI Efl_Future *efl_future_iterator_race(Eina_Iterator *it);
110#include "efl_io_stderr.eo.h" 110#include "efl_io_stderr.eo.h"
111#include "efl_io_file.eo.h" 111#include "efl_io_file.eo.h"
112#include "efl_io_copier.eo.h" 112#include "efl_io_copier.eo.h"
113#include "efl_io_buffered_stream.eo.h"
113 114
114/** 115/**
115 * @} 116 * @}
diff --git a/src/lib/ecore/efl_io_buffered_stream.c b/src/lib/ecore/efl_io_buffered_stream.c
new file mode 100644
index 0000000..f75c033
--- /dev/null
+++ b/src/lib/ecore/efl_io_buffered_stream.c
@@ -0,0 +1,549 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#define EFL_IO_READER_PROTECTED 1
6#define EFL_IO_WRITER_PROTECTED 1
7#define EFL_IO_CLOSER_PROTECTED 1
8
9#include <Ecore.h>
10#include "ecore_private.h"
11
12typedef struct
13{
14 Eo *inner_io;
15 Eo *incoming;
16 Eo *outgoing;
17 Eo *sender;
18 Eo *receiver;
19 Eina_Bool closed;
20 Eina_Bool eos;
21 Eina_Bool can_read;
22 Eina_Bool can_write;
23 Eina_Bool is_closer;
24} Efl_Io_Buffered_Stream_Data;
25
26#define MY_CLASS EFL_IO_BUFFERED_STREAM_CLASS
27
28static void
29_efl_io_buffered_stream_error(void *data, const Efl_Event *event)
30{
31 Eo *o = data;
32 Eina_Error *perr = event->info;
33 DBG("%p %s error: %s", o, efl_name_get(event->object), eina_error_msg_get(*perr));
34 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_ERROR, event->info);
35}
36
37static void
38_efl_io_buffered_stream_incoming_can_read_changed(void *data, const Efl_Event *event)
39{
40 Eo *o = data;
41 if (efl_io_closer_closed_get(o)) return; /* already closed (or closing) */
42 efl_io_reader_can_read_set(o, efl_io_reader_can_read_get(event->object));
43}
44
45static void
46_efl_io_buffered_stream_incoming_slice_changed(void *data, const Efl_Event *event EINA_UNUSED)
47{
48 Eo *o = data;
49 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, NULL);
50}
51
52EFL_CALLBACKS_ARRAY_DEFINE(_efl_io_buffered_stream_incoming_cbs,
53 { EFL_IO_READER_EVENT_CAN_READ_CHANGED, _efl_io_buffered_stream_incoming_can_read_changed },
54 { EFL_IO_QUEUE_EVENT_SLICE_CHANGED, _efl_io_buffered_stream_incoming_slice_changed });
55
56static void
57_efl_io_buffered_stream_receiver_line(void *data, const Efl_Event *event)
58{
59 Eo *o = data;
60 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_LINE, event->info);
61}
62
63static void
64_efl_io_buffered_stream_receiver_done(void *data, const Efl_Event *event EINA_UNUSED)
65{
66 Eo *o = data;
67 if (efl_io_closer_closed_get(o)) return; /* already closed (or closing) */
68 efl_io_reader_eos_set(o, EINA_TRUE);
69}
70
71EFL_CALLBACKS_ARRAY_DEFINE(_efl_io_buffered_stream_receiver_cbs,
72 { EFL_IO_COPIER_EVENT_DONE, _efl_io_buffered_stream_receiver_done },
73 { EFL_IO_COPIER_EVENT_LINE, _efl_io_buffered_stream_receiver_line },
74 { EFL_IO_COPIER_EVENT_ERROR, _efl_io_buffered_stream_error });
75
76
77static void
78_efl_io_buffered_stream_outgoing_can_write_changed(void *data, const Efl_Event *event)
79{
80 Eo *o = data;
81 if (efl_io_closer_closed_get(o)) return; /* already closed (or closing) */
82 efl_io_writer_can_write_set(o, efl_io_writer_can_write_get(event->object));
83}
84
85EFL_CALLBACKS_ARRAY_DEFINE(_efl_io_buffered_stream_outgoing_cbs,
86 { EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _efl_io_buffered_stream_outgoing_can_write_changed });
87
88static void
89_efl_io_buffered_stream_sender_done(void *data, const Efl_Event *event EINA_UNUSED)
90{
91 Eo *o = data;
92 Efl_Io_Buffered_Stream_Data *pd = efl_data_scope_get(o, MY_CLASS);
93 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, NULL);
94 if (efl_io_copier_done_get(pd->receiver))
95 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, NULL);
96}
97
98EFL_CALLBACKS_ARRAY_DEFINE(_efl_io_buffered_stream_sender_cbs,
99 { EFL_IO_COPIER_EVENT_DONE, _efl_io_buffered_stream_sender_done },
100 { EFL_IO_COPIER_EVENT_ERROR, _efl_io_buffered_stream_error });
101
102static void
103_efl_io_buffered_stream_inner_io_del(void *data, const Efl_Event *event)
104{
105 Eo *o = data;
106 Efl_Io_Buffered_Stream_Data *pd = efl_data_scope_get(o, MY_CLASS);
107 DBG("%p the inner I/O %p was deleted", o, event->object);
108 if (pd->inner_io == event->object)
109 pd->inner_io = NULL;
110}
111
112EFL_CALLBACKS_ARRAY_DEFINE(_efl_io_buffered_stream_inner_io_cbs,
113 { EFL_EVENT_DEL, _efl_io_buffered_stream_inner_io_del });
114
115
116EOLIAN static Efl_Object *
117_efl_io_buffered_stream_efl_object_finalize(Eo *o, Efl_Io_Buffered_Stream_Data *pd)
118{
119 if (!pd->inner_io)
120 {
121 ERR("no valid I/O was set with efl_io_buffered_stream_inner_io_set()!");
122 return NULL;
123 }
124
125 return efl_finalize(efl_super(o, MY_CLASS));
126}
127
128EOLIAN static void
129_efl_io_buffered_stream_efl_object_destructor(Eo *o, Efl_Io_Buffered_Stream_Data *pd)
130{
131 if (pd->incoming)
132 {
133 efl_del(pd->incoming);
134 pd->incoming = NULL;
135 }
136 if (pd->outgoing)
137 {
138 efl_del(pd->outgoing);
139 pd->outgoing = NULL;
140 }
141 if (pd->sender)
142 {
143 efl_del(pd->sender);
144 pd->sender = NULL;
145 }
146 if (pd->receiver)
147 {
148 efl_del(pd->receiver);
149 pd->receiver = NULL;
150 }
151
152 if (pd->inner_io)
153 {
154 efl_event_callback_array_del(pd->inner_io, _efl_io_buffered_stream_inner_io_cbs(), o);
155 efl_unref(pd->inner_io); /* do not del, just take our ref */
156 pd->inner_io = NULL;
157 }
158
159 efl_destructor(efl_super(o, MY_CLASS));
160}
161
162EOLIAN static Eina_Error
163_efl_io_buffered_stream_efl_io_closer_close(Eo *o, Efl_Io_Buffered_Stream_Data *pd)
164{
165 Eina_Error err = 0;
166
167 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->closed, EINVAL);
168
169 /* line delimiters may be holding a last chunk of data */
170 if (pd->receiver) efl_io_copier_flush(pd->receiver, EINA_FALSE, EINA_TRUE);
171
172 efl_io_writer_can_write_set(o, EINA_FALSE);
173 efl_io_reader_can_read_set(o, EINA_FALSE);
174 efl_io_reader_eos_set(o, EINA_TRUE);
175
176 pd->closed = EINA_TRUE;
177 efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
178
179 if (pd->sender && (!efl_io_closer_closed_get(pd->sender)))
180 efl_io_closer_close(pd->sender);
181
182 if (pd->receiver && (!efl_io_closer_closed_get(pd->receiver)))
183 efl_io_closer_close(pd->receiver);
184
185 return err;
186}
187
188EOLIAN static Eina_Bool
189_efl_io_buffered_stream_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
190{
191 return pd->closed || efl_io_closer_closed_get(pd->inner_io);
192}
193
194EOLIAN static Eina_Bool
195_efl_io_buffered_stream_efl_io_closer_close_on_exec_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
196{
197 return efl_io_closer_close_on_exec_get(pd->inner_io);
198}
199
200EOLIAN static Eina_Bool
201_efl_io_buffered_stream_efl_io_closer_close_on_exec_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, Eina_Bool value)
202{
203 return efl_io_closer_close_on_exec_set(pd->inner_io, value);
204}
205
206EOLIAN static Eina_Bool
207_efl_io_buffered_stream_efl_io_closer_close_on_destructor_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
208{
209 return efl_io_closer_close_on_destructor_get(pd->inner_io);
210}
211
212EOLIAN static void
213_efl_io_buffered_stream_efl_io_closer_close_on_destructor_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, Eina_Bool value)
214{
215 efl_io_closer_close_on_destructor_set(pd->inner_io, value);
216}
217
218EOLIAN static Eina_Error
219_efl_io_buffered_stream_efl_io_reader_read(Eo *o, Efl_Io_Buffered_Stream_Data *pd, Eina_Rw_Slice *rw_slice)
220{
221 Eina_Error err;
222
223 if (!pd->incoming)
224 {
225 WRN("%p reading from inner_io %p (%s) that doesn't implement Efl.Io.Reader",
226 o, pd->inner_io, efl_class_name_get(efl_class_get(pd->inner_io)));
227 return EINVAL;
228 }
229
230 err = efl_io_reader_read(pd->incoming, rw_slice);
231 if (err && (err != EAGAIN))
232 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_ERROR, &err);
233 return err;
234}
235
236EOLIAN static Eina_Bool
237_efl_io_buffered_stream_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
238{
239 return pd->can_read;
240}
241
242EOLIAN static void
243_efl_io_buffered_stream_efl_io_reader_can_read_set(Eo *o, Efl_Io_Buffered_Stream_Data *pd EINA_UNUSED, Eina_Bool can_read)
244{
245 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
246 if (pd->can_read == can_read) return;
247 pd->can_read = can_read;
248 efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
249}
250
251EOLIAN static Eina_Bool
252_efl_io_buffered_stream_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
253{
254 return pd->eos;
255}
256
257EOLIAN static void
258_efl_io_buffered_stream_efl_io_reader_eos_set(Eo *o, Efl_Io_Buffered_Stream_Data *pd, Eina_Bool is_eos)
259{
260 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
261 if (pd->eos == is_eos) return;
262 pd->eos = is_eos;
263 if (!is_eos) return;
264
265 efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
266 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, NULL);
267 if (efl_io_copier_done_get(pd->sender))
268 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, NULL);
269}
270
271EOLIAN static Eina_Error
272_efl_io_buffered_stream_efl_io_writer_write(Eo *o, Efl_Io_Buffered_Stream_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
273{
274 Eina_Error err;
275
276 if (!pd->outgoing)
277 {
278 WRN("%p writing to inner_io %p (%s) that doesn't implement Efl.Io.Writer",
279 o, pd->inner_io, efl_class_name_get(efl_class_get(pd->inner_io)));
280 return EINVAL;
281 }
282
283 err = efl_io_writer_write(pd->outgoing, slice, remaining);
284 if (err && (err != EAGAIN))
285 efl_event_callback_call(o, EFL_IO_BUFFERED_STREAM_EVENT_ERROR, &err);
286 return err;
287}
288
289EOLIAN static Eina_Bool
290_efl_io_buffered_stream_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
291{
292 return pd->can_write;
293}
294
295EOLIAN static void
296_efl_io_buffered_stream_efl_io_writer_can_write_set(Eo *o, Efl_Io_Buffered_Stream_Data *pd EINA_UNUSED, Eina_Bool can_write)
297{
298 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
299 if (pd->can_write == can_write) return;
300 pd->can_write = can_write;
301 efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
302}
303
304EOLIAN static void
305_efl_io_buffered_stream_inner_io_set(Eo *o, Efl_Io_Buffered_Stream_Data *pd, Efl_Object *io)
306{
307 Eina_Bool is_reader, is_writer;
308
309 EINA_SAFETY_ON_TRUE_RETURN(efl_finalized_get(o));
310 EINA_SAFETY_ON_NULL_RETURN(io);
311 EINA_SAFETY_ON_TRUE_RETURN(pd->inner_io != NULL);
312
313 pd->is_closer = efl_isa(io, EFL_IO_CLOSER_MIXIN);
314 is_reader = efl_isa(io, EFL_IO_READER_INTERFACE);
315 is_writer = efl_isa(io, EFL_IO_WRITER_INTERFACE);
316
317 EINA_SAFETY_ON_TRUE_RETURN((!is_reader) && (!is_writer));
318
319 pd->inner_io = efl_ref(io);
320 efl_event_callback_array_add(io, _efl_io_buffered_stream_inner_io_cbs(), o);
321
322 /* inner_io -> incoming */
323 if (is_reader)
324 {
325 DBG("%p inner_io=%p (%s) is Efl.Io.Reader", o, io, efl_class_name_get(efl_class_get(io)));
326 pd->incoming = efl_add(EFL_IO_QUEUE_CLASS, o,
327 efl_name_set(efl_added, "incoming"),
328 efl_event_callback_array_add(efl_added, _efl_io_buffered_stream_incoming_cbs(), o));
329 EINA_SAFETY_ON_NULL_RETURN(pd->incoming);
330
331 pd->receiver = efl_add(EFL_IO_COPIER_CLASS, o,
332 efl_name_set(efl_added, "receiver"),
333 efl_io_copier_buffer_limit_set(efl_added, 4096),
334 efl_io_copier_source_set(efl_added, io),
335 efl_io_copier_destination_set(efl_added, pd->incoming),
336 efl_io_closer_close_on_destructor_set(efl_added, EINA_FALSE),
337 efl_event_callback_array_add(efl_added, _efl_io_buffered_stream_receiver_cbs(), o));
338 EINA_SAFETY_ON_NULL_RETURN(pd->receiver);
339 }
340 else
341 {
342 DBG("%p inner_io=%p (%s) is not Efl.Io.Reader", o, io, efl_class_name_get(efl_class_get(io)));
343 efl_io_reader_eos_set(o, EINA_TRUE);
344 }
345
346
347 /* outgoing -> inner_io */
348 if (is_writer)
349 {
350 DBG("%p inner_io=%p (%s) is Efl.Io.Writer", o, io, efl_class_name_get(efl_class_get(io)));
351 pd->outgoing = efl_add(EFL_IO_QUEUE_CLASS, o,
352 efl_name_set(efl_added, "outgoing"),
353 efl_event_callback_array_add(efl_added, _efl_io_buffered_stream_outgoing_cbs(), o));
354 EINA_SAFETY_ON_NULL_RETURN(pd->outgoing);
355
356 pd->sender = efl_add(EFL_IO_COPIER_CLASS, o,
357 efl_name_set(efl_added, "sender"),
358 efl_io_copier_buffer_limit_set(efl_added, 4096),
359 efl_io_copier_source_set(efl_added, pd->outgoing),
360 efl_io_copier_destination_set(efl_added, io),
361 efl_io_closer_close_on_destructor_set(efl_added, EINA_FALSE),
362 efl_event_callback_array_add(efl_added, _efl_io_buffered_stream_sender_cbs(), o));
363 EINA_SAFETY_ON_NULL_RETURN(pd->sender);
364 }
365 else
366 DBG("%p inner_io=%p (%s) is not Efl.Io.Writer", o, io, efl_class_name_get(efl_class_get(io)));
367}
368
369EOLIAN static Efl_Object *
370_efl_io_buffered_stream_inner_io_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
371{
372 return pd->inner_io;
373}
374
375EOLIAN static void
376_efl_io_buffered_stream_max_queue_size_input_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, size_t max_queue_size_input)
377{
378 if (!pd->incoming)
379 {
380 DBG("%p inner_io=%p (%s) is not Efl.Io.Reader, limit=%zu ignored", o, pd->inner_io, efl_class_name_get(efl_class_get(pd->inner_io)), max_queue_size_input);
381 return;
382 }
383 efl_io_queue_limit_set(pd->incoming, max_queue_size_input);
384}
385
386EOLIAN static size_t
387_efl_io_buffered_stream_max_queue_size_input_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
388{
389 if (!pd->incoming) return 0;
390 return efl_io_queue_limit_get(pd->incoming);
391}
392
393EOLIAN static void
394_efl_io_buffered_stream_max_queue_size_output_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, size_t max_queue_size_output)
395{
396 if (!pd->outgoing)
397 {
398 DBG("%p inner_io=%p (%s) is not Efl.Io.Writer, limit=%zu ignored", o, pd->inner_io, efl_class_name_get(efl_class_get(pd->inner_io)), max_queue_size_output);
399 return;
400 }
401 efl_io_queue_limit_set(pd->outgoing, max_queue_size_output);
402}
403
404EOLIAN static size_t
405_efl_io_buffered_stream_max_queue_size_output_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
406{
407 if (!pd->outgoing) return 0;
408 return efl_io_queue_limit_get(pd->outgoing);
409}
410
411EOLIAN static void
412_efl_io_buffered_stream_line_delimiter_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, const Eina_Slice *slice)
413{
414 if (!pd->receiver)
415 {
416 DBG("%p inner_io=%p (%s) is not Efl.Io.Reader, slice=%p ignored", o, pd->inner_io, efl_class_name_get(efl_class_get(pd->inner_io)), slice);
417 return;
418 }
419 efl_io_copier_line_delimiter_set(pd->receiver, slice);
420}
421
422EOLIAN static const Eina_Slice *
423_efl_io_buffered_stream_line_delimiter_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
424{
425 if (!pd->receiver) return NULL;
426 return efl_io_copier_line_delimiter_get(pd->receiver);
427}
428
429EOLIAN static void
430_efl_io_buffered_stream_inactivity_timeout_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, double seconds)
431{
432 if (pd->receiver)
433 efl_io_copier_inactivity_timeout_set(pd->receiver, seconds);
434 if (pd->sender)
435 efl_io_copier_inactivity_timeout_set(pd->sender, seconds);
436}
437
438EOLIAN static double
439_efl_io_buffered_stream_inactivity_timeout_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
440{
441 if (pd->receiver)
442 return efl_io_copier_inactivity_timeout_get(pd->receiver);
443 if (pd->sender)
444 return efl_io_copier_inactivity_timeout_get(pd->sender);
445 return 0.0;
446}
447
448EOLIAN static void
449_efl_io_buffered_stream_read_chunk_size_set(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, size_t size)
450{
451 if (pd->sender)
452 {
453 efl_io_copier_buffer_limit_set(pd->sender, size);
454 efl_io_copier_read_chunk_size_set(pd->sender, size);
455 }
456
457 if (!pd->receiver)
458 {
459 efl_io_copier_buffer_limit_set(pd->receiver, size);
460 efl_io_copier_read_chunk_size_set(pd->receiver, size);
461 }
462}
463
464EOLIAN static size_t
465_efl_io_buffered_stream_read_chunk_size_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
466{
467 if (!pd->receiver) return 0;
468 return efl_io_copier_read_chunk_size_get(pd->receiver);
469}
470
471EOLIAN static size_t
472_efl_io_buffered_stream_pending_write_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
473{
474 if (!pd->outgoing) return 0;
475 return efl_io_queue_usage_get(pd->outgoing);
476}
477
478EOLIAN static size_t
479_efl_io_buffered_stream_pending_read_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
480{
481 if (!pd->incoming) return 0;
482 return efl_io_queue_usage_get(pd->incoming);
483}
484
485EOLIAN static Eina_Bool
486_efl_io_buffered_stream_slice_get(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, Eina_Slice *slice)
487{
488 if (!pd->incoming)
489 {
490 if (slice)
491 {
492 slice->mem = NULL;
493 slice->len = 0;
494 }
495 return EINA_FALSE;
496 }
497 return efl_io_queue_slice_get(pd->incoming, slice);
498}
499
500EOLIAN static void
501_efl_io_buffered_stream_discard(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd, size_t amount)
502{
503 if (!pd->incoming) return;
504 efl_io_queue_discard(pd->incoming, amount);
505}
506
507EOLIAN static void
508_efl_io_buffered_stream_clear(Eo *o EINA_UNUSED, Efl_Io_Buffered_Stream_Data *pd)
509{
510 if (!pd->incoming) return;
511 efl_io_queue_clear(pd->incoming);
512}
513
514EOLIAN static void
515_efl_io_buffered_stream_eos_mark(Eo *o, Efl_Io_Buffered_Stream_Data *pd)
516{
517 if (!pd->incoming) return;
518 DBG("%p mark eos", o);
519 efl_io_queue_eos_mark(pd->outgoing);
520}
521
522EOLIAN static Eina_Bool
523_efl_io_buffered_stream_flush(Eo *o, Efl_Io_Buffered_Stream_Data *pd, Eina_Bool may_block, Eina_Bool ignore_line_delimiter)
524{
525 size_t pending;
526 Eina_Bool ret;
527
528 EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE);
529
530 if (!pd->outgoing) return EINA_TRUE;
531
532 pending = efl_io_queue_usage_get(pd->outgoing);
533 if (!pending)
534 return EINA_TRUE;
535
536 if (pd->is_closer && efl_io_closer_closed_get(pd->inner_io))
537 {
538 DBG("%p the inner I/O %p is already closed", o, pd->inner_io);
539 return EINA_TRUE;
540 }
541
542 DBG("%p attempt to flush %zu bytes, may_block=%hhu, ignore_line_delimiter=%hhu...", o, pending, may_block, ignore_line_delimiter);
543 ret = efl_io_copier_flush(pd->sender, may_block, ignore_line_delimiter);
544 DBG("%p flushed, ret=%hhu, still pending=%zu", o, ret, efl_io_queue_usage_get(pd->outgoing));
545
546 return ret;
547}
548
549#include "efl_io_buffered_stream.eo.c"
diff --git a/src/lib/ecore/efl_io_buffered_stream.eo b/src/lib/ecore/efl_io_buffered_stream.eo
new file mode 100644
index 0000000..28049c4
--- /dev/null
+++ b/src/lib/ecore/efl_io_buffered_stream.eo
@@ -0,0 +1,251 @@
1class Efl.Io.Buffered_Stream (Efl.Loop_User, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer) {
2 [[A wrapper object offering an easy to use, buffered streams over existing I/O class.
3
4 The buffered stream encapsulates an actual @Efl.Io.Reader or
5 @Efl.Io.Writer, an input @Efl.Io.Queue, an output @Efl.Io.Queue
6 and these are linked using a input and a output
7 @Efl.Io.Copier.
8
9 The idea is that unlike traditional @Efl.Io.Writer that will
10 attempt to write directly and thus may take less data than
11 requested, this one will keep the pending data in its own
12 buffer, feeding to the actual output when it
13 @Efl.Io.Writer.can_write. That makes its operation much simpler
14 as @Efl.Io.Writer.write will always take the full data -- allows
15 "write and forget", if unlimited (see
16 @.max_queue_size_output). When finished writing data, the
17 @.eos_mark and then wait for "write,finished" event to know when all data
18 was sent.
19
20 Reading is also much simpler since incoming data is kept in an
21 @Efl.Io.Queue, thus its size can be queried with @.pending_read
22 and read with @Efl.Io.Reader.read or peeked with @.slice_get,
23 then discarded with @.discard or @.clear.
24
25 Then when waiting for a complete message, just peek at its
26 contents, if not complete do nothing and wait, if complete then
27 either @Efl.Io.Reader.read to get a copy or manipulate a
28 read-only reference from @.slice_get and then @.discard
29
30 The actual I/O is set with the constructor method @.inner_io.set
31 and can be retrieved with @.inner_io.get, which should be used
32 with care -- calling @Efl.Io.Reader.read and
33 @Efl.Io.Writer.write on it may produce unexpected results.
34
35 @since 1.19
36 ]]
37
38 methods {
39 @property inner_io {
40 [[The inner I/O this wrapper operates on.]]
41 get {
42 [[The internal input/output used for actual operations, use with care!]]
43 }
44 set {
45 [[Constructor-only property to set the inner_io.]]
46 }
47 values {
48 io: Efl.Object; [[The input (@Efl.Io.Reader) or output (@Efl.Io.Writer) instance]]
49 }
50 }
51
52 @property max_queue_size_input {
53 [[Limit how big the input queue can grow, in bytes.
54
55 If limited and @.line_delimiter is set, "line" events
56 may be emitted with partial contents, without the
57 trailing delimiter.
58 ]]
59 get { }
60 set {
61 [[Constructor-only property to set buffer limit. 0 is unlimited]]
62 }
63 values {
64 max_queue_size_input: size; [[Defines a maximum buffer size for incoming data, or 0 to allow unlimited amount of bytes]]
65 }
66 }
67
68 @property max_queue_size_output {
69 [[Limit how big the output queue can grow, in bytes.
70
71
72 If limited, @Efl.Io.Writer.write will take less data than requested!
73 ]]
74 get { }
75 set {
76 [[Constructor-only property to set buffer limit. 0 is unlimited]]
77 }
78 values {
79 max_queue_size_output: size; [[Defines a maximum buffer size for output data, or 0 to allow unlimited amount of bytes. If limited, @Efl.Io.Writer.write will take less data than requested!]]
80 }
81 }
82
83 @property line_delimiter {
84 [[If set, incoming data will be checked for the delimiter and "line" events are The line may include the delimiter, unless it's end-of-stream on @.max_queue_size_input was reached.]]
85 get { }
86 set {
87 [[Change line delimiter to use. If NULL or empty, no delimiter is to be used]]
88 }
89 values {
90 slice: ptr(const(Eina.Slice)); [[The contents may contain \0 and will be copied]]
91 }
92 }
93
94 @property inactivity_timeout {
95 [[Error as ETIMEDOUT if it becomes inactive for some time.
96
97 If no activity, that is no read or write in the given
98 amount of seconds, then the object will emit "error"
99 event with ETIMEDOUT value.
100
101 This is specified in seconds and is only active for
102 greater-than zero. Defaults to inactive.
103 ]]
104 values {
105 seconds: double; [[Number inactive seconds to timeout this object. If zero or less, it will be disabled.]]
106 }
107 }
108
109 @property read_chunk_size {
110 [[Read chunk size property, in bytes.
111
112 When reading the @.inner_io for data to be placed in
113 input queue, use this as chunk size.
114
115 Setting this value large enough may reduce number of
116 @Efl.Io.Reader.read, improving performance at the expense
117 of more memory consumption.
118
119 This value is bounded by @.max_queue_size_input if it's set.
120
121 By default it's 4096.
122 ]]
123 get {
124 }
125 set {
126 [[Set chunk size for each basic @Efl.Io.Reader.read operation.]]
127 }
128 values {
129 size: size; [[This is the chunk size to use for read operations]]
130 }
131 }
132
133 @property pending_write {
134 [[How many bytes are pending write to @.inner_io]]
135 get { }
136 values {
137 usage: size; [[Bytes available to write]]
138 }
139 }
140
141 @property pending_read {
142 [[How many bytes are pending (available) for read]]
143 get { }
144 values {
145 usage: size; [[Bytes available to read]]
146 }
147 }
148
149 slice_get { // TODO: property and return of Eina.Slice (not pointer)
150 [[Get a temporary access to input queue's internal read memory.
151
152 The memory pointed by slice may be changed by other
153 methods of this class. The event "slice,changed" will be
154 called in those situations.
155 ]]
156 params {
157 @out slice: Eina.Slice; [[Slice of the current buffer, may be invalidated if @Efl.Io.Writer.write, @Efl.Io.Closer.close or @Efl.Io.Reader.read are called. It is the full slice available for reading.]]
158 }
159 return: bool (false); [[$true on success, $false otherwise]]
160 }
161
162 discard {
163 [[Discard the given number of bytes.
164
165 This has the same effect as reading and discarding the
166 given amount of bytes, without executing the actual
167 copy.
168
169 It's often paired with @.slice_get, if users read the
170 information from the slice and once they're done, that
171 data must be discarded.
172
173 As an example, some protocols provide messages with a
174 "size" header, then @.slice_get is used to peek into the
175 available memory to see if there is a "size" and if the
176 rest of the slice is the full payload, in this case the
177 slice may be handled to some processing function. When
178 the function is done, that amount of data must be
179 discarded -- with this function.
180 ]]
181 params {
182 amount: size; [[Bytes to discard]]
183 }
184 }
185
186 clear {
187 [[Clear the incoming queue. Same as reading all data.
188
189 This is equivalent as calling @.discard with @.pending_read
190 amount of bytes.
191 ]]
192 }
193
194 eos_mark {
195 [[Mark this end-of-stream, signals nothing else will be written.
196
197 That will forbid any further writes.
198
199 Unlike @Efl.Io.Closer.close, this won't clear anything.
200
201 When all data is written, "write,finished" is emitted.
202 ]]
203 }
204
205 flush {
206 [[Forces writing all pending data to destination.
207
208 It will return $true if @.pending_read drops to zero, $false
209 otherwise and more calls to flush must be made.
210
211 If the @.inner_io is implements @Efl.Io.Closer and it
212 was closed, or the wrapper itself was closed, this
213 function will do nothing and returns $true.
214
215 \@note this function may block the main loop execution
216 until operations complete! This is bad for usability, as
217 user interface or other operations may freeze. A better
218 approach is to operate asynchronously and wait for
219 "write,finished" event.
220 ]]
221 params {
222 may_block: bool; [[If $true, then @Efl.Io.Reader.can_read and @Efl.Io.Writer.can_write are not checked and the call may block.]]
223 ignore_line_delimiter: bool; [[Force flush ignoring line delimiters]]
224 }
225 return: bool(true); [[$true if all data was sent, $false otherwise]]
226 }
227 }
228
229 events {
230 write,finished; [[@.eos_mark was called and all available data was sent to destination]]
231 read,finished; [[Same as @Efl.Io.Reader "eos", for consistency.]]
232 finished; [[Both read and write are finished.]]
233 error: Eina.Error; [[An error happened and the I/O stopped]]
234 slice,changed; [[The read-slice returned by @.slice_get may have changed.]]
235 line: ptr(const(Eina.Slice)); [[If @.line_delimiter is set, will be emitted with current line. The memory is only valid during event callback dispatched and should not be modified. Note that the line slice may not be inside @.slice_get, don't assume that!]]
236 }
237
238 implements {
239 Efl.Object.finalize;
240 Efl.Object.destructor;
241 Efl.Io.Closer.close;
242 Efl.Io.Closer.closed.get;
243 Efl.Io.Closer.close_on_exec;
244 Efl.Io.Closer.close_on_destructor;
245 Efl.Io.Reader.read;
246 Efl.Io.Reader.can_read;
247 Efl.Io.Reader.eos;
248 Efl.Io.Writer.write;
249 Efl.Io.Writer.can_write;
250 }
251}