summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-08-23 20:03:10 -0300
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-08-23 20:17:13 -0300
commit86e87b2fd932a8199683f7bd1adfaba1c9f43d9a (patch)
tree1696c0acff294ed4b270dc5e7026004cad118b63
parenta1526169e7925677ef9c9eaf742ef1c8d8d13bf6 (diff)
efl_io_queue: basic class to interact with Efl.Io interfaces.
The use of low-level interfaces such as Efl.Io.Reader and Efl.Io.Writer are not that user-friendly as they can handle partial data. Classes such as Efl.Io.Copier makes them easy to use, but they need a reader (source) or writer (destination) and in our examples we used fixed buffers or some existing streams (stdin/stdout/stderr, networking...). However, if interactively we need to produce some data to be sent, such as implementing some networking protocols, we'd have to write our own Efl.Io.Reader and Efl.Io.Writer classes to handle the buffering. Not anymore! With Efl.Io.Queue you can write stuff to it and it will buffer to memory. Once stuff is read, it will automatically remove those bytes from buffer.
-rw-r--r--src/Makefile_Efl.am2
-rw-r--r--src/examples/ecore/.gitignore1
-rw-r--r--src/examples/ecore/Makefile.am5
-rw-r--r--src/examples/ecore/efl_io_queue_example.c374
-rw-r--r--src/lib/efl/Efl.h1
-rw-r--r--src/lib/efl/interfaces/efl_io_queue.c430
-rw-r--r--src/lib/efl/interfaces/efl_io_queue.eo94
7 files changed, 907 insertions, 0 deletions
diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am
index 2ba7bf0ec4..a66ab6bd4d 100644
--- a/src/Makefile_Efl.am
+++ b/src/Makefile_Efl.am
@@ -52,6 +52,7 @@ efl_eolian_files = \
52 lib/efl/interfaces/efl_io_sizer.eo \ 52 lib/efl/interfaces/efl_io_sizer.eo \
53 lib/efl/interfaces/efl_io_writer.eo \ 53 lib/efl/interfaces/efl_io_writer.eo \
54 lib/efl/interfaces/efl_io_buffer.eo \ 54 lib/efl/interfaces/efl_io_buffer.eo \
55 lib/efl/interfaces/efl_io_queue.eo \
55 $(efl_eolian_legacy_files) \ 56 $(efl_eolian_legacy_files) \
56 $(NULL) 57 $(NULL)
57 58
@@ -100,6 +101,7 @@ lib/efl/interfaces/efl_io_reader.c \
100lib/efl/interfaces/efl_io_sizer.c \ 101lib/efl/interfaces/efl_io_sizer.c \
101lib/efl/interfaces/efl_io_writer.c \ 102lib/efl/interfaces/efl_io_writer.c \
102lib/efl/interfaces/efl_io_buffer.c \ 103lib/efl/interfaces/efl_io_buffer.c \
104lib/efl/interfaces/efl_io_queue.c \
103$(NULL) 105$(NULL)
104 106
105lib_efl_libefl_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl -I$(top_srcdir)/src/lib/efl @EFL_CFLAGS@ -DEFL_GFX_FILTER_BETA 107lib_efl_libefl_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl -I$(top_srcdir)/src/lib/efl @EFL_CFLAGS@ -DEFL_GFX_FILTER_BETA
diff --git a/src/examples/ecore/.gitignore b/src/examples/ecore/.gitignore
index bd92dcd3a8..38a72c7e24 100644
--- a/src/examples/ecore/.gitignore
+++ b/src/examples/ecore/.gitignore
@@ -48,5 +48,6 @@
48/ecore_buffer_provider_example 48/ecore_buffer_provider_example
49/efl_io_copier_example 49/efl_io_copier_example
50/efl_io_copier_simple_example 50/efl_io_copier_simple_example
51/efl_io_queue_example
51/efl_net_server_example 52/efl_net_server_example
52/efl_net_dialer_http_example 53/efl_net_dialer_http_example
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index de4292c297..16a8693319 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -79,6 +79,7 @@ ecore_con_eet_client_example \
79ecore_con_eet_server_example \ 79ecore_con_eet_server_example \
80efl_io_copier_example \ 80efl_io_copier_example \
81efl_io_copier_simple_example \ 81efl_io_copier_simple_example \
82efl_io_queue_example \
82efl_net_server_example \ 83efl_net_server_example \
83efl_net_dialer_http_example 84efl_net_dialer_http_example
84 85
@@ -287,6 +288,9 @@ efl_io_copier_example_LDADD = $(ECORE_CON_COMMON_LDADD)
287efl_io_copier_simple_example_SOURCES = efl_io_copier_simple_example.c 288efl_io_copier_simple_example_SOURCES = efl_io_copier_simple_example.c
288efl_io_copier_simple_example_LDADD = $(ECORE_COMMON_LDADD) 289efl_io_copier_simple_example_LDADD = $(ECORE_COMMON_LDADD)
289 290
291efl_io_queue_example_SOURCES = efl_io_queue_example.c
292efl_io_queue_example_LDADD = $(ECORE_CON_COMMON_LDADD)
293
290efl_net_server_example_SOURCES = efl_net_server_example.c 294efl_net_server_example_SOURCES = efl_net_server_example.c
291efl_net_server_example_LDADD = $(ECORE_CON_COMMON_LDADD) 295efl_net_server_example_LDADD = $(ECORE_CON_COMMON_LDADD)
292 296
@@ -341,6 +345,7 @@ ecore_con_eet_server_example.c \
341ecore_con_eet_descriptor_example.c \ 345ecore_con_eet_descriptor_example.c \
342efl_io_copier_example.c \ 346efl_io_copier_example.c \
343efl_io_copier_simple_example.c \ 347efl_io_copier_simple_example.c \
348efl_io_queue_example.c \
344efl_net_server_example.c \ 349efl_net_server_example.c \
345efl_net_dialer_http_example.c 350efl_net_dialer_http_example.c
346 351
diff --git a/src/examples/ecore/efl_io_queue_example.c b/src/examples/ecore/efl_io_queue_example.c
new file mode 100644
index 0000000000..71fa8468bd
--- /dev/null
+++ b/src/examples/ecore/efl_io_queue_example.c
@@ -0,0 +1,374 @@
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 *waiting = NULL;
9static Eina_List *commands = NULL;
10static Eina_Slice line_delimiter;
11static Eo *send_queue, *receive_queue;
12
13static void
14_command_next(void)
15{
16 Eina_Slice slice;
17 char *cmd;
18
19 if (!commands)
20 {
21 efl_io_queue_eos_mark(send_queue);
22 return;
23 }
24
25 cmd = commands->data;
26 commands = eina_list_remove_list(commands, commands);
27
28 slice = (Eina_Slice)EINA_SLICE_STR(cmd);
29 efl_io_writer_write(send_queue, &slice, NULL);
30 fprintf(stderr, "INFO: sent '" EINA_SLICE_STR_FMT "'\n",
31 EINA_SLICE_STR_PRINT(slice));
32
33 /* don't use line_delimiter directly, 'len' may be changed! */
34 slice = line_delimiter;
35 efl_io_writer_write(send_queue, &slice, NULL);
36 free(cmd);
37}
38
39static void
40_receiver_data(void *data EINA_UNUSED, const Eo_Event *event)
41{
42 Eina_Slice slice;
43
44 if (!efl_io_queue_slice_get(event->object, &slice)) return;
45
46 /* this will happen when we're called when we issue our own
47 * efl_io_queue_clear() below.
48 */
49 if (slice.len == 0) return;
50
51 if (slice.len < line_delimiter.len)
52 {
53 fprintf(stderr, "ERROR: received short line '" EINA_SLICE_STR_FMT "'\n",
54 EINA_SLICE_STR_PRINT(slice));
55 }
56 else if (memcmp(slice.bytes + slice.len - line_delimiter.len,
57 line_delimiter.bytes, line_delimiter.len) != 0)
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_queue_clear(event->object);
71 _command_next();
72}
73
74static void
75_dialer_connected(void *data EINA_UNUSED, const Eo_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_copier_done(void *data EINA_UNUSED, const Eo_Event *event)
86{
87 fprintf(stderr, "INFO: %s done\n", efl_name_get(event->object));
88
89 waiting = eina_list_remove(waiting, event->object);
90 if (!waiting)
91 ecore_main_loop_quit();
92}
93
94static void
95_copier_error(void *data EINA_UNUSED, const Eo_Event *event)
96{
97 const Eina_Error *perr = event->info;
98 fprintf(stderr, "INFO: %s error: #%d '%s'\n",
99 efl_name_get(event->object), *perr, eina_error_msg_get(*perr));
100 retval = EXIT_FAILURE;
101 ecore_main_loop_quit();
102}
103
104EFL_CALLBACKS_ARRAY_DEFINE(copier_cbs,
105 { EFL_IO_COPIER_EVENT_DONE, _copier_done },
106 { EFL_IO_COPIER_EVENT_ERROR, _copier_error });
107
108static char *
109_unescape(const char *str)
110{
111 char *ret = strdup(str);
112 char *c, *w;
113 Eina_Bool escaped = EINA_FALSE;
114
115 for (c = ret, w = ret; *c != '\0'; c++)
116 {
117 if (escaped)
118 {
119 escaped = EINA_FALSE;
120 switch (*c)
121 {
122 case 'n': *w = '\n'; break;
123 case 'r': *w = '\r'; break;
124 case 't': *w = '\t'; break;
125 default: w++; /* no change */
126 }
127 w++;
128 }
129 else
130 {
131 if (*c == '\\')
132 escaped = EINA_TRUE;
133 else
134 w++;
135 }
136 }
137 *w = '\0';
138 return ret;
139}
140
141static const Ecore_Getopt options = {
142 "efl_io_queue_example", /* program name */
143 NULL, /* usage line */
144 "1", /* version */
145 "(C) 2016 Enlightenment Project", /* copyright */
146 "BSD 2-Clause", /* license */
147 /* long description, may be multiline and contain \n */
148 "Example of Efl_Io_Queue usage.\n"
149 "\n"
150 "This uses Efl_Io_Queue to easily interface with Efl_Io_Copier in order to "
151 "send commands to a TCP server.",
152 EINA_FALSE,
153 {
154 ECORE_GETOPT_STORE_STR('d', "line-delimiter",
155 "Changes the line delimiter to be used in both send and receive. Defaults to \\r\\n"),
156 ECORE_GETOPT_STORE_ULONG('l', "buffer-limit",
157 "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."),
158 ECORE_GETOPT_VERSION('V', "version"),
159 ECORE_GETOPT_COPYRIGHT('C', "copyright"),
160 ECORE_GETOPT_LICENSE('L', "license"),
161 ECORE_GETOPT_HELP('h', "help"),
162
163 ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
164 "The server address as\n"
165 "IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n",
166 "server_address"),
167 ECORE_GETOPT_APPEND_METAVAR(0, NULL,
168 "Commands to send",
169 "commands",
170 ECORE_GETOPT_TYPE_STR),
171 ECORE_GETOPT_SENTINEL
172 }
173};
174
175int
176main(int argc, char **argv)
177{
178 char *address = NULL;
179 char *line_delimiter_str = NULL;
180 char *cmd;
181 unsigned long buffer_limit = 0;
182 Eina_Bool quit_option = EINA_FALSE;
183 Ecore_Getopt_Value values[] = {
184 ECORE_GETOPT_VALUE_STR(line_delimiter_str),
185 ECORE_GETOPT_VALUE_ULONG(buffer_limit),
186
187 /* standard block to provide version, copyright, license and help */
188 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
189 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
190 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
191 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
192
193 /* positional argument */
194 ECORE_GETOPT_VALUE_STR(address),
195 ECORE_GETOPT_VALUE_LIST(commands),
196
197 ECORE_GETOPT_VALUE_NONE /* sentinel */
198 };
199 Eina_Error err;
200 int args;
201 Eo *dialer, *sender, *receiver, *loop;
202
203 ecore_init();
204 ecore_con_init();
205
206 args = ecore_getopt_parse(&options, values, argc, argv);
207 if (args < 0)
208 {
209 fputs("ERROR: Could not parse command line options.\n", stderr);
210 retval = EXIT_FAILURE;
211 goto end;
212 }
213
214 if (quit_option) goto end;
215
216 args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
217 if (args < 0)
218 {
219 fputs("ERROR: Could not parse positional arguments.\n", stderr);
220 retval = EXIT_FAILURE;
221 goto end;
222 }
223
224 line_delimiter_str = _unescape(line_delimiter_str ? line_delimiter_str : "\\r\\n");
225
226 if (!commands)
227 {
228 fputs("ERROR: missing commands to send.\n", stderr);
229 retval = EXIT_FAILURE;
230 goto end;
231 }
232
233 line_delimiter = (Eina_Slice)EINA_SLICE_STR(line_delimiter_str);
234
235 /*
236 * Without a send_queue we'd have to manually implement an
237 * Efl_Io_Reader object that would provide partial data when
238 * Efl_Io_Reader.read() is called by Efl_Io_Copier. This is
239 * cumbersome... we just want to write a full command and have the
240 * queue to handle that for us.
241 *
242 * Our example's usage is to write each command at once followed by
243 * the line_delimiter, then wait for a reply from the server, then
244 * write another.
245 */
246 send_queue = efl_add(EFL_IO_QUEUE_CLASS, NULL,
247 efl_name_set(efl_self, "send_queue"),
248 efl_io_queue_limit_set(efl_self, buffer_limit));
249 if (!send_queue)
250 {
251 fprintf(stderr, "ERROR: could not create Efl_Io_Queue (send)\n");
252 retval = EXIT_FAILURE;
253 goto end;
254 }
255
256 /*
257 * Without a receive_queue we'd have to manually implement an
258 * Efl_Io_Writer object that would handle write of partial data
259 * with Efl_Io_Writer.write() is called by Efl_Io_Copier.
260 *
261 * For output we could have another solution as well: use NULL
262 * destination and handle "line" or "data" events manually,
263 * stealing the buffer so it doesn't grow.
264 *
265 * Our example's usage is to peek its data with slice_get() then
266 * clear().
267 */
268 receive_queue = efl_add(EFL_IO_QUEUE_CLASS, NULL,
269 efl_name_set(efl_self, "receive_queue"),
270 efl_io_queue_limit_set(efl_self, buffer_limit),
271 efl_event_callback_add(efl_self, EFL_IO_QUEUE_EVENT_SLICE_CHANGED, _receiver_data, NULL));
272 if (!receive_queue)
273 {
274 fprintf(stderr, "ERROR: could not create Efl_Io_Queue (receive)\n");
275 retval = EXIT_FAILURE;
276 goto error_receive_queue;
277 }
278
279 /*
280 * From here on it's mostly the same all Efl_Io_Copier would do,
281 * check efl_io_copier_simple_example.c and efl_io_copier_example.c
282 */
283
284 /*
285 * some objects such as the Efl.Io.Copier and Efl.Net.Dialer.Tcp
286 * depend on main loop, thus their parent must be a loop
287 * provider. We use the loop itself.
288 */
289 loop = ecore_main_loop_get();
290
291 /* The TCP client to use to send/receive network data */
292 dialer = efl_add(EFL_NET_DIALER_TCP_CLASS, loop,
293 efl_name_set(efl_self, "dialer"),
294 efl_event_callback_add(efl_self, EFL_NET_DIALER_EVENT_CONNECTED, _dialer_connected, NULL));
295 if (!dialer)
296 {
297 fprintf(stderr, "ERROR: could not create Efl_Net_Dialer_Tcp\n");
298 retval = EXIT_FAILURE;
299 goto error_dialer;
300 }
301
302 /* sender: send_queue->network */
303 sender = efl_add(EFL_IO_COPIER_CLASS, loop,
304 efl_name_set(efl_self, "sender"),
305 efl_io_copier_line_delimiter_set(efl_self, &line_delimiter),
306 efl_io_copier_source_set(efl_self, send_queue),
307 efl_io_copier_destination_set(efl_self, dialer),
308 efl_event_callback_array_add(efl_self, copier_cbs(), NULL));
309 if (!sender)
310 {
311 fprintf(stderr, "ERROR: could not create Efl_Io_Copier (sender)\n");
312 retval = EXIT_FAILURE;
313 goto error_sender;
314 }
315
316 /* receiver: network->receive_queue */
317 receiver = efl_add(EFL_IO_COPIER_CLASS, loop,
318 efl_name_set(efl_self, "receiver"),
319 efl_io_copier_line_delimiter_set(efl_self, &line_delimiter),
320 efl_io_copier_source_set(efl_self, dialer),
321 efl_io_copier_destination_set(efl_self, receive_queue),
322 efl_event_callback_array_add(efl_self, copier_cbs(), NULL));
323 if (!receiver)
324 {
325 fprintf(stderr, "ERROR: could not create Efl_Io_Copier (receiver)\n");
326 retval = EXIT_FAILURE;
327 goto error_receiver;
328 }
329
330 err = efl_net_dialer_dial(dialer, address);
331 if (err)
332 {
333 fprintf(stderr, "ERROR: could not dial %s: %s\n",
334 address, eina_error_msg_get(err));
335 goto error_dialing;
336 }
337
338 waiting = eina_list_append(waiting, sender);
339 waiting = eina_list_append(waiting, receiver);
340
341 ecore_main_loop_begin();
342
343 if (waiting)
344 {
345 fprintf(stderr, "ERROR: %d operations were waiting!\n",
346 eina_list_count(waiting));
347 eina_list_free(waiting);
348 waiting = NULL;
349 }
350
351 error_dialing:
352 efl_io_closer_close(receiver);
353 efl_del(receiver);
354 error_receiver:
355 efl_io_closer_close(sender);
356 efl_del(sender);
357 error_sender:
358 efl_del(dialer);
359 error_dialer:
360 efl_del(receive_queue);
361 error_receive_queue:
362 efl_del(send_queue);
363 end:
364 EINA_LIST_FREE(commands, cmd)
365 {
366 fprintf(stderr, "ERROR: unsent command: %s\n", cmd);
367 free(cmd);
368 }
369
370 ecore_con_shutdown();
371 ecore_shutdown();
372
373 return retval;
374}
diff --git a/src/lib/efl/Efl.h b/src/lib/efl/Efl.h
index 38f7bab70b..79733afc02 100644
--- a/src/lib/efl/Efl.h
+++ b/src/lib/efl/Efl.h
@@ -137,6 +137,7 @@ EAPI extern const Efl_Event_Description _EFL_GFX_PATH_CHANGED;
137#include "interfaces/efl_io_positioner.eo.h" 137#include "interfaces/efl_io_positioner.eo.h"
138 138
139#include "interfaces/efl_io_buffer.eo.h" 139#include "interfaces/efl_io_buffer.eo.h"
140#include "interfaces/efl_io_queue.eo.h"
140 141
141#else 142#else
142 143
diff --git a/src/lib/efl/interfaces/efl_io_queue.c b/src/lib/efl/interfaces/efl_io_queue.c
new file mode 100644
index 0000000000..a4dcf3216b
--- /dev/null
+++ b/src/lib/efl/interfaces/efl_io_queue.c
@@ -0,0 +1,430 @@
1#define EFL_IO_READER_PROTECTED 1
2#define EFL_IO_WRITER_PROTECTED 1
3
4#include "config.h"
5#include "Efl.h"
6
7#define MY_CLASS EFL_IO_QUEUE_CLASS
8
9/*
10 * This queue is simple and based on a single buffer that is
11 * reallocated as needed up to some limit, keeping some pre-allocated
12 * amount of bytes.
13 *
14 * Writes appends to the buffer. Reads consume and remove data from
15 * buffer head.
16 *
17 * To avoid too much memmove(), reads won't immediately remove data,
18 * instead will only increment position_read and allow some
19 * slack. When the slack limit is reached or the buffer needs more
20 * memory for write, then the memmove() happens.
21 *
22 * A more complex and possibly efficient version of this would be to
23 * keep a list of internal buffers of fixed size. Writing would result
24 * into segment and write into these chunks, creating new if
25 * needed. Reading would consume from multiple chunks and if they're
26 * all used, would be freed.
27 */
28
29typedef struct _Efl_Io_Queue_Data
30{
31 uint8_t *bytes;
32 size_t allocated;
33 size_t preallocated;
34 size_t limit;
35 size_t position_read; /* to avoid memmove(), allows some slack */
36 size_t position_write;
37 Eina_Bool pending_eos;
38 Eina_Bool eos;
39 Eina_Bool closed;
40 Eina_Bool can_read;
41 Eina_Bool can_write;
42} Efl_Io_Queue_Data;
43
44static Eina_Bool
45_efl_io_queue_realloc(Eo *o, Efl_Io_Queue_Data *pd, size_t size)
46{
47 void *tmp;
48 size_t limit = efl_io_queue_limit_get(o);
49
50 if ((limit > 0) && (size > limit))
51 size = limit;
52
53 if (pd->allocated == size) return EINA_FALSE;
54
55 if (size == 0)
56 {
57 free(pd->bytes);
58 tmp = NULL;
59 }
60 else
61 {
62 tmp = realloc(pd->bytes, size);
63 EINA_SAFETY_ON_NULL_RETURN_VAL(tmp, EINA_FALSE);
64 }
65
66 pd->bytes = tmp;
67 pd->allocated = size;
68 return EINA_TRUE;
69}
70
71static size_t
72_efl_io_queue_slack_get(const Efl_Io_Queue_Data *pd)
73{
74 const size_t used = pd->position_write - pd->position_read;
75
76 if (used >= 4096) return 4096;
77 else if (used >= 1024) return 1024;
78 else if (used >= 128) return 128;
79 else return 32;
80}
81
82static Eina_Bool
83_efl_io_queue_realloc_rounded(Eo *o, Efl_Io_Queue_Data *pd, size_t size)
84{
85 if ((size > 0) && (size < 128))
86 size = ((size / 32) + 1) * 32;
87 else if (size < 1024)
88 size = ((size / 128) + 1) * 128;
89 else if (size < 8192)
90 size = ((size / 1024) + 1) * 1024;
91 else
92 size = ((size / 4096) + 1) * 4096;
93
94 return _efl_io_queue_realloc(o, pd, size);
95}
96
97/* reset position_read to zero, allowing all memory for write */
98static void
99_efl_io_queue_adjust(Efl_Io_Queue_Data *pd)
100{
101 size_t used = pd->position_write - pd->position_read;
102 memmove(pd->bytes, pd->bytes + pd->position_read, used);
103 pd->position_write = used;
104 pd->position_read = 0;
105}
106
107static void
108_efl_io_queue_adjust_and_realloc_if_needed(Eo *o, Efl_Io_Queue_Data *pd)
109{
110 const size_t slack = _efl_io_queue_slack_get(pd);
111 size_t spare;
112
113 if (pd->limit > 0)
114 {
115 if (pd->position_write + slack >= pd->limit)
116 _efl_io_queue_adjust(pd);
117 }
118 else if (pd->position_read > slack)
119 _efl_io_queue_adjust(pd);
120
121 spare = pd->allocated - pd->position_write;
122 if (spare > slack)
123 {
124 size_t new_size = pd->position_write + slack;
125
126 /*
127 * this may result in going over slack again, no
128 * problems with that.
129 */
130 if (new_size < pd->preallocated)
131 new_size = pd->preallocated;
132
133 /* use rounded so we avoid too many reallocs */
134 _efl_io_queue_realloc_rounded(o, pd, new_size);
135 }
136}
137
138static void
139_efl_io_queue_update_cans(Eo *o, Efl_Io_Queue_Data *pd)
140{
141 size_t used = pd->position_write - pd->position_read;
142 size_t limit;
143
144 efl_io_reader_can_read_set(o, used > 0);
145
146 limit = efl_io_queue_limit_get(o);
147 if (pd->pending_eos)
148 efl_io_writer_can_write_set(o, EINA_FALSE);
149 else
150 efl_io_writer_can_write_set(o, (limit == 0) || (used < limit));
151}
152
153EOLIAN static void
154_efl_io_queue_preallocate(Eo *o, Efl_Io_Queue_Data *pd, size_t size)
155{
156 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
157 if (pd->allocated < size)
158 _efl_io_queue_realloc_rounded(o, pd, size);
159 pd->preallocated = size;
160}
161
162EOLIAN static void
163_efl_io_queue_limit_set(Eo *o, Efl_Io_Queue_Data *pd, size_t limit)
164{
165 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
166
167 if (pd->limit == limit) return;
168 pd->limit = limit;
169 if (pd->limit == 0) goto end;
170
171 _efl_io_queue_adjust(pd);
172
173 if (pd->allocated > limit)
174 _efl_io_queue_realloc(o, pd, limit);
175
176 if (pd->position_write > limit)
177 {
178 pd->position_write = limit;
179 if (pd->position_read > limit) pd->position_read = limit;
180 }
181
182 _efl_io_queue_adjust_and_realloc_if_needed(o, pd);
183 efl_event_callback_call(o, EFL_IO_QUEUE_EVENT_SLICE_CHANGED, NULL);
184
185 end:
186 _efl_io_queue_update_cans(o, pd);
187}
188
189EOLIAN static size_t
190_efl_io_queue_limit_get(Eo *o EINA_UNUSED, Efl_Io_Queue_Data *pd)
191{
192 return pd->limit;
193}
194
195EOLIAN static size_t
196_efl_io_queue_usage_get(Eo *o EINA_UNUSED, Efl_Io_Queue_Data *pd)
197{
198 return pd->position_write - pd->position_read;
199}
200
201EOLIAN static Eina_Bool
202_efl_io_queue_slice_get(Eo *o, Efl_Io_Queue_Data *pd, Eina_Slice *slice)
203{
204 if (slice)
205 {
206 slice->mem = pd->bytes + pd->position_read;
207 slice->len = efl_io_queue_usage_get(o);
208 }
209 EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE);
210 return EINA_TRUE;
211}
212
213EOLIAN static void
214_efl_io_queue_clear(Eo *o, Efl_Io_Queue_Data *pd)
215{
216 pd->position_read = 0;
217 pd->position_write = 0;
218 efl_io_reader_can_read_set(o, EINA_FALSE);
219 efl_event_callback_call(o, EFL_IO_QUEUE_EVENT_SLICE_CHANGED, NULL);
220 if (pd->pending_eos)
221 efl_io_reader_eos_set(o, EINA_TRUE);
222}
223
224EOLIAN static void
225_efl_io_queue_eos_mark(Eo *o, Efl_Io_Queue_Data *pd)
226{
227 if (pd->eos) return;
228
229 if (efl_io_queue_usage_get(o) > 0)
230 pd->pending_eos = EINA_TRUE;
231 else
232 efl_io_reader_eos_set(o, EINA_TRUE);
233}
234
235EOLIAN static Efl_Object *
236_efl_io_queue_efl_object_finalize(Eo *o, Efl_Io_Queue_Data *pd EINA_UNUSED)
237{
238 o = efl_finalize(efl_super(o, MY_CLASS));
239 if (!o) return NULL;
240
241 _efl_io_queue_update_cans(o, pd);
242
243 return o;
244}
245
246EOLIAN static void
247_efl_io_queue_efl_object_destructor(Eo *o, Efl_Io_Queue_Data *pd)
248{
249 if (!efl_io_closer_closed_get(o))
250 efl_io_closer_close(o);
251
252 efl_destructor(efl_super(o, MY_CLASS));
253
254 if (pd->bytes)
255 {
256 free(pd->bytes);
257 pd->bytes = NULL;
258 pd->allocated = 0;
259 pd->position_read = 0;
260 pd->position_write = 0;
261 }
262}
263
264EOLIAN static Eina_Error
265_efl_io_queue_efl_io_reader_read(Eo *o, Efl_Io_Queue_Data *pd, Eina_Rw_Slice *rw_slice)
266{
267 Eina_Slice ro_slice;
268 size_t available;
269
270 EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
271 EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
272
273 available = pd->position_write - pd->position_read;
274 if (rw_slice->len > available)
275 {
276 rw_slice->len = available;
277 if (rw_slice->len == 0)
278 return EAGAIN;
279 }
280
281 ro_slice.len = rw_slice->len;
282 ro_slice.mem = pd->bytes + pd->position_read;
283
284 *rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
285 pd->position_read += ro_slice.len;
286
287 efl_io_reader_can_read_set(o, pd->position_read < pd->position_write);
288 efl_event_callback_call(o, EFL_IO_QUEUE_EVENT_SLICE_CHANGED, NULL);
289
290 if ((pd->pending_eos) && (efl_io_queue_usage_get(o) == 0))
291 efl_io_reader_eos_set(o, EINA_TRUE);
292
293 return 0;
294
295 error:
296 rw_slice->len = 0;
297 return EINVAL;
298}
299
300EOLIAN static Eina_Bool
301_efl_io_queue_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Io_Queue_Data *pd)
302{
303 return pd->can_read;
304}
305
306EOLIAN static void
307_efl_io_queue_efl_io_reader_can_read_set(Eo *o, Efl_Io_Queue_Data *pd, Eina_Bool can_read)
308{
309 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
310 if (pd->can_read == can_read) return;
311 pd->can_read = can_read;
312 efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
313}
314
315EOLIAN static Eina_Bool
316_efl_io_queue_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Io_Queue_Data *pd EINA_UNUSED)
317{
318 return pd->eos;
319}
320
321EOLIAN static void
322_efl_io_queue_efl_io_reader_eos_set(Eo *o, Efl_Io_Queue_Data *pd EINA_UNUSED, Eina_Bool is_eos)
323{
324 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
325 if (pd->eos == is_eos) return;
326 pd->eos = is_eos;
327 if (is_eos)
328 {
329 pd->pending_eos = EINA_FALSE;
330 efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
331 }
332}
333
334EOLIAN static Eina_Error
335_efl_io_queue_efl_io_writer_write(Eo *o, Efl_Io_Queue_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
336{
337 size_t available_write, available_total, todo, limit;
338 int err = EINVAL;
339
340 EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
341 EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
342
343 err = EBADF;
344 EINA_SAFETY_ON_TRUE_GOTO(pd->pending_eos, error);
345
346 available_write = pd->allocated - pd->position_write;
347 available_total = available_write + pd->position_read;
348 limit = efl_io_queue_limit_get(o);
349
350 err = ENOSPC;
351 if (available_write >= slice->len)
352 {
353 todo = slice->len;
354 }
355 else if (available_total >= slice->len)
356 {
357 _efl_io_queue_adjust(pd);
358 todo = slice->len;
359 }
360 else if ((limit > 0) && (pd->allocated == limit)) goto error;
361 else
362 {
363 _efl_io_queue_adjust(pd);
364 _efl_io_queue_realloc_rounded(o, pd, pd->position_write + slice->len);
365 if (pd->allocated >= pd->position_write + slice->len)
366 todo = slice->len;
367 else
368 todo = pd->allocated - pd->position_write;
369
370 if (todo == 0) goto error;
371 }
372
373 memcpy(pd->bytes + pd->position_write, slice->mem, todo);
374 if (remaining)
375 {
376 remaining->len = slice->len - todo;
377 if (remaining->len)
378 remaining->mem = slice->bytes + todo;
379 else
380 remaining->mem = NULL;
381 }
382 slice->len = todo;
383
384 pd->position_write += todo;
385
386 _efl_io_queue_adjust_and_realloc_if_needed(o, pd);
387 efl_event_callback_call(o, EFL_IO_QUEUE_EVENT_SLICE_CHANGED, NULL);
388 _efl_io_queue_update_cans(o, pd);
389
390 return 0;
391
392 error:
393 if (remaining) *remaining = *slice;
394 slice->len = 0;
395 return err;
396}
397
398EOLIAN static Eina_Bool
399_efl_io_queue_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Io_Queue_Data *pd)
400{
401 return pd->can_write;
402}
403
404EOLIAN static void
405_efl_io_queue_efl_io_writer_can_write_set(Eo *o, Efl_Io_Queue_Data *pd, Eina_Bool can_write)
406{
407 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
408 if (pd->can_write == can_write) return;
409 pd->can_write = can_write;
410 efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
411}
412
413EOLIAN static Eina_Error
414_efl_io_queue_efl_io_closer_close(Eo *o, Efl_Io_Queue_Data *pd)
415{
416 EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL);
417 efl_io_queue_eos_mark(o);
418 efl_io_queue_clear(o);
419 pd->closed = EINA_TRUE;
420 efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
421 return 0;
422}
423
424EOLIAN static Eina_Bool
425_efl_io_queue_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Io_Queue_Data *pd)
426{
427 return pd->closed;
428}
429
430#include "interfaces/efl_io_queue.eo.c"
diff --git a/src/lib/efl/interfaces/efl_io_queue.eo b/src/lib/efl/interfaces/efl_io_queue.eo
new file mode 100644
index 0000000000..9ce3f0aa40
--- /dev/null
+++ b/src/lib/efl/interfaces/efl_io_queue.eo
@@ -0,0 +1,94 @@
1class Efl.Io.Queue (Efl.Object, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer) {
2 [[Generic In-memory queue of data to be used as I/O.
3
4 This class is to be used to receive temporary data using
5 @Efl.Io.Writer.write and hold it until someone calls
6 @Efl.Io.Reader.read to consume it.
7
8 A fixed sized queue can be implemented by setting @.limit
9 followed by @.preallocate
10
11 @since 1.19
12 ]]
13
14 methods {
15 preallocate {
16 [[Immediately pre-allocate a buffer of at least a given size.]]
17 params {
18 @in size: size; [[amount of bytes to pre-allocate.]]
19 }
20 }
21
22 @property limit {
23 [[Limit how big the buffer can grow.
24
25 This affects both @.preallocate and how buffer grows
26 when @Efl.Io.Writer.write is called.
27
28 If you want a buffer of an exact size, always set the
29 limit before any further calls that can grow it.
30 ]]
31 get { }
32 set {
33 [[Constructor-only property to set buffer limit. 0 is unlimited]]
34 }
35 values {
36 size: size; [[Defines a maximum buffer size, or 0 to allow unlimited amount of bytes]]
37 }
38 }
39
40 @property usage {
41 [[How many bytes are available for read]]
42 get { }
43 values {
44 usage: size;
45 }
46 }
47
48 slice_get { // TODO: property and return of Eina.Slice (not pointer)
49 [[Get a temporary access to queue's internal read memory.
50
51 The memory pointed by slice may be changed by other
52 methods of this class. The event "slice,changed" will be
53 called in those situations.
54 ]]
55 params {
56 @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.]]
57 }
58 return: bool (false);
59 }
60
61 clear {
62 [[Clear the queue. Same as reading all data]]
63 }
64
65 eos_mark {
66 [[Mark this end-of-stream.
67
68 That will set @Efl.Io.Reader.eos to $true and forbid any
69 further writes.
70
71 Unlike @Efl.Io.Closer.close, this won't clear anything.
72 ]]
73 }
74 }
75
76 events {
77 slice,changed; [[The read-slice returned by @.slice_get may have changed.]]
78 }
79
80 implements {
81 Efl.Object.finalize;
82 Efl.Object.destructor;
83 Efl.Io.Reader.read;
84 Efl.Io.Reader.can_read.get;
85 Efl.Io.Reader.can_read.set;
86 Efl.Io.Reader.eos.get;
87 Efl.Io.Reader.eos.set;
88 Efl.Io.Writer.write;
89 Efl.Io.Writer.can_write.get;
90 Efl.Io.Writer.can_write.set;
91 Efl.Io.Closer.close;
92 Efl.Io.Closer.closed.get;
93 }
94}