efl.io: introduce basic interfaces and classes.

These interfaces allows generic operations on objects that can store
or provide data, such as a file or a buffer.

With well defined interfaces and events we can create code such as
Efl.Io.Copier, that will link a source with a destination and
progressively copy data as they appear.
This commit is contained in:
Gustavo Sverzut Barbieri 2016-08-13 04:50:29 -03:00
parent 0d96ba9734
commit 5d4688679e
39 changed files with 3230 additions and 2 deletions

View File

@ -10,6 +10,16 @@ ecore_eolian_files = \
lib/ecore/efl_loop.eo \
lib/ecore/efl_loop_user.eo \
lib/ecore/efl_loop_fd.eo \
lib/ecore/efl_io_closer_fd.eo \
lib/ecore/efl_io_positioner_fd.eo \
lib/ecore/efl_io_reader_fd.eo \
lib/ecore/efl_io_sizer_fd.eo \
lib/ecore/efl_io_writer_fd.eo \
lib/ecore/efl_io_stdin.eo \
lib/ecore/efl_io_stdout.eo \
lib/ecore/efl_io_stderr.eo \
lib/ecore/efl_io_file.eo \
lib/ecore/efl_io_copier.eo \
lib/ecore/ecore_parent.eo \
$(ecore_eolian_files_legacy)
@ -59,6 +69,16 @@ lib/ecore/ecore_job.c \
lib/ecore/ecore_main.c \
lib/ecore/efl_loop_user.c \
lib/ecore/efl_loop_fd.c \
lib/ecore/efl_io_closer_fd.c \
lib/ecore/efl_io_positioner_fd.c \
lib/ecore/efl_io_reader_fd.c \
lib/ecore/efl_io_sizer_fd.c \
lib/ecore/efl_io_writer_fd.c \
lib/ecore/efl_io_stdin.c \
lib/ecore/efl_io_stdout.c \
lib/ecore/efl_io_stderr.c \
lib/ecore/efl_io_file.c \
lib/ecore/efl_io_copier.c \
lib/ecore/ecore_pipe.c \
lib/ecore/ecore_poller.c \
lib/ecore/ecore_time.c \

View File

@ -46,6 +46,12 @@ efl_eolian_files = \
lib/efl/interfaces/efl_input_interface.eo \
lib/efl/interfaces/efl_input_state.eo \
lib/efl/interfaces/efl_screen.eo \
lib/efl/interfaces/efl_io_closer.eo \
lib/efl/interfaces/efl_io_positioner.eo \
lib/efl/interfaces/efl_io_reader.eo \
lib/efl/interfaces/efl_io_sizer.eo \
lib/efl/interfaces/efl_io_writer.eo \
lib/efl/interfaces/efl_io_buffer.eo \
$(efl_eolian_legacy_files) \
$(NULL)
@ -88,6 +94,12 @@ lib/efl/interfaces/efl_vpath_manager.c \
lib/efl/interfaces/efl_vpath_core.c \
lib/efl/interfaces/efl_vpath_file_core.c \
lib/efl/interfaces/efl_input_device.c \
lib/efl/interfaces/efl_io_closer.c \
lib/efl/interfaces/efl_io_positioner.c \
lib/efl/interfaces/efl_io_reader.c \
lib/efl/interfaces/efl_io_sizer.c \
lib/efl/interfaces/efl_io_writer.c \
lib/efl/interfaces/efl_io_buffer.c \
$(NULL)
lib_efl_libefl_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl -I$(top_srcdir)/src/lib/efl @EFL_CFLAGS@ -DEFL_GFX_FILTER_BETA

View File

@ -46,3 +46,4 @@
/ecore_buffer_example
/ecore_buffer_consumer_example
/ecore_buffer_provider_example
/efl_io_copier_example

View File

@ -76,7 +76,8 @@ ecore_time_functions_example \
ecore_timer_example \
ecore_getopt_example \
ecore_con_eet_client_example \
ecore_con_eet_server_example
ecore_con_eet_server_example \
efl_io_copier_example
ECORE_COMMON_LDADD = \
$(top_builddir)/src/lib/ecore/libecore.la \
@ -276,6 +277,10 @@ ecore_con_eet_server_example_SOURCES = ecore_con_eet_server_example.c \
ecore_con_eet_server_example_LDADD = $(ECORE_CON_COMMON_LDADD) \
$(top_builddir)/src/lib/eet/libeet.la
efl_io_copier_example_SOURCES = efl_io_copier_example.c
efl_io_copier_example_LDADD = $(ECORE_CON_COMMON_LDADD)
SRCS = \
ecore_animator_example.c \
ecore_buffer_example.c \
@ -321,7 +326,8 @@ ecore_timer_example.c \
ecore_getopt_example.c \
ecore_con_eet_client_example.c \
ecore_con_eet_server_example.c \
ecore_con_eet_descriptor_example.c
ecore_con_eet_descriptor_example.c \
efl_io_copier_example.c
DATA_FILES = red.png Makefile.examples

View File

@ -0,0 +1,476 @@
#define EFL_BETA_API_SUPPORT 1
#define EFL_EO_API_SUPPORT 1
#include <Ecore.h>
#include <Ecore_Getopt.h>
#include <fcntl.h>
static int retval = EXIT_SUCCESS;
/* NOTE: input events are only used as debug, you can omit these */
static void
_input_can_read_changed(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: input can_read=%d\n",
efl_io_reader_can_read_get(event->object));
}
static void
_input_eos(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: input eos=%d\n",
efl_io_reader_eos_get(event->object));
}
EFL_CALLBACKS_ARRAY_DEFINE(input_cbs,
{ EFL_IO_READER_EVENT_CAN_READ_CHANGED, _input_can_read_changed },
{ EFL_IO_READER_EVENT_EOS, _input_eos });
/* NOTE: output events are only used as debug, you can omit these */
static void
_output_can_write_changed(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: output can_write=%d\n",
efl_io_writer_can_write_get(event->object));
}
EFL_CALLBACKS_ARRAY_DEFINE(output_cbs,
{ EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _output_can_write_changed });
static void
_output_buffer_reallocated(void *data EINA_UNUSED, const Eo_Event *event)
{
Eina_Slice slice;
if (!efl_io_buffer_slice_get(event->object, &slice))
{
fprintf(stderr, "ERROR: could not get buffer slice\n");
return;
}
fprintf(stderr, "INFO: output buffer reallocated=" EINA_SLICE_FMT "\n",
EINA_SLICE_PRINT(slice));
}
EFL_CALLBACKS_ARRAY_DEFINE(output_buffer_cbs,
{ EFL_IO_BUFFER_EVENT_REALLOCATED, _output_buffer_reallocated });
/* copier events are of interest, you should hook to at least "done"
* and "error"
*/
static void
_copier_done(void *data EINA_UNUSED, const Eo_Event *event)
{
Eo *destination = efl_io_copier_destination_get(event->object);
if (!destination)
{
/* if :none: was used, you get Efl_Io_Copier to buffer all
* source data up to efl_io_copier_buffer_limit_get().
*
* Then if it finished, you can steal the binbuf and do
* something with that. It's a simple way to use
* Efl_Io_Copier.
*/
Eina_Binbuf *binbuf = efl_io_copier_binbuf_steal(event->object);
fprintf(stderr,
"INFO: :none: resulted in binbuf=%p, string=%p, size=%zd:"
"\n--BEGIN DATA--\n"
EINA_SLICE_STR_FMT
"\n--END DATA--\n",
binbuf,
eina_binbuf_string_get(binbuf),
eina_binbuf_length_get(binbuf),
EINA_SLICE_STR_PRINT(eina_binbuf_slice_get(binbuf))
);
eina_binbuf_free(binbuf);
}
else if (efl_isa(destination, EFL_IO_BUFFER_CLASS))
{
/* if :memory: was used, then an Efl_Io_Buffer is created
* implementing all the interfaces required by Efl_Io_Copier.
*
* This allows the buffer to be resized if source size is
* known, avoiding realloc()s during execution.
*
* You can use:
*
* - efl_io_buffer_slice_get() for a read-only view of that
* buffer,
*
* - efl_io_buffer_binbuf_steal() to take the buffer memory as
* an Eina_Binbuf, you own the ownership and must call
* eina_binbuf_free() to release it.
*/
Eina_Slice slice;
if (!efl_io_buffer_slice_get(destination, &slice))
fprintf(stderr, "ERROR: could not get buffer slice\n");
else
fprintf(stderr,
"INFO: :memory: resulted in slice=" EINA_SLICE_FMT ":"
"\n--BEGIN DATA--\n"
EINA_SLICE_STR_FMT
"\n--END DATA--\n",
EINA_SLICE_PRINT(slice), EINA_SLICE_STR_PRINT(slice));
}
fprintf(stderr, "INFO: done\n");
ecore_main_loop_quit();
}
static void
_copier_error(void *data EINA_UNUSED, const Eo_Event *event)
{
const Eina_Error *perr = event->info;
fprintf(stderr, "INFO: error: %d\n", *perr);
retval = EXIT_FAILURE;
ecore_main_loop_quit();
}
static void
_copier_progress(void *data EINA_UNUSED, const Eo_Event *event)
{
uint64_t r, w, t;
efl_io_copier_progress_get(event->object, &r, &w, &t);
/* if total is zero, that means the source object doesn't provide a
* fixed size, consider it a "stream" such as a socket, a pipe,
* stdin...
*/
if (t == 0)
fprintf(stderr, "INFO: read=%" PRIu64 ", written=%" PRIu64 "\n", r, w);
else
{
fprintf(stderr,
"INFO: read=%" PRIu64 ", written=%" PRIu64 ", total=%" PRIu64
" (%4.1f%%/%4.1f%%)\n",
r, w, t,
(100.0 * r) / (double)t,
(100.0 * w) / (double)t);
}
}
static void
_copier_data(void *data EINA_UNUSED, const Eo_Event *event)
{
const Eina_Slice *slice = event->info;
/* a piece of data was processed, it's ready-only and will only be
* good for immediate consumption.
*
* It's only usable inside this function as it may be gone (freed,
* reallocated) once it return and more data is processed.
*
* Shall you want to take over the internal binbuf, use
* efl_io_copier_binbuf_steal() and then stop event propagation
* with efl_event_callback_stop().
*
* However be aware that other events will be dispatched with empty
* slices, like if you steal the buffer here, _copier_line() will
* get empty slice.
*/
fprintf(stderr, "INFO: data: " EINA_SLICE_FMT "\n",
EINA_SLICE_PRINT(*slice));
}
static void
_copier_line(void *data EINA_UNUSED, const Eo_Event *event)
{
const Eina_Slice *slice = event->info;
/* a line_delimiter was provided and a line was processed, it's
* ready-only and will only be good for immediate consumption.
*
* It's only usable inside this function as it may be gone (freed,
* reallocated) once it return and more data is processed.
*
* The line may not contain the line delimiter in the following
* cases:
*
* - efl_io_copier_buffer_limit_set() was used and limit was
* reached.
*
* - source reached end-of-stream and pending data was there.
*
* Shall you want to take over the internal binbuf, use
* efl_io_copier_binbuf_steal() and then stop event propagation
* with efl_event_callback_stop().
*/
fprintf(stderr, "INFO: line: " EINA_SLICE_STR_FMT "\n",
EINA_SLICE_STR_PRINT(*slice));
}
EFL_CALLBACKS_ARRAY_DEFINE(copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _copier_error },
{ EFL_IO_COPIER_EVENT_PROGRESS, _copier_progress },
{ EFL_IO_COPIER_EVENT_DATA, _copier_data},
{ EFL_IO_COPIER_EVENT_LINE, _copier_line});
static const Ecore_Getopt options = {
"efl_io_copier_example", /* program name */
NULL, /* usage line */
"1", /* version */
"(C) 2016 Enlightenment Project", /* copyright */
"BSD 2-Clause", /* license */
/* long description, may be multiline and contain \n */
"Example of Efl_Io_Copier usage.\n"
"\n"
"This example copies from an Efl_Io_Reader to an Efl_Io_Writer, listening"
"for events and showing progress.",
EINA_FALSE,
{
ECORE_GETOPT_STORE_STR('d', "line-delimiter",
"If set will define a line delimiter for copy operation, instead of a fixed chunk size. This will trigger line events."),
ECORE_GETOPT_STORE_ULONG('l', "buffer-limit",
"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."),
ECORE_GETOPT_STORE_ULONG('c', "read-chunk-size",
"If set will change the base chunk size used while reading."),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
"The input file name or ':stdin:' to read from stdin.",
"input-file"),
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
"The output file name or:\n"
":stdout: to write to stdout.\n"
":stderr: to write to stderr.\n"
":memory: to write to a memory buffer.\n"
":none: to not use a destination object.\n"
"",
"output-file"),
ECORE_GETOPT_SENTINEL
}
};
int
main(int argc, char **argv)
{
char *input_fname = NULL;
char *output_fname = NULL;
char *line_delimiter = NULL;
unsigned long buffer_limit = 0;
unsigned long read_chunk_size = 0;
Eina_Bool quit_option = EINA_FALSE;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_STR(line_delimiter),
ECORE_GETOPT_VALUE_ULONG(buffer_limit),
ECORE_GETOPT_VALUE_ULONG(read_chunk_size),
/* standard block to provide version, copyright, license and help */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
/* positional argument */
ECORE_GETOPT_VALUE_STR(input_fname),
ECORE_GETOPT_VALUE_STR(output_fname),
ECORE_GETOPT_VALUE_NONE /* sentinel */
};
int args;
Eo *input, *output, *copier;
Eina_Slice line_delm_slice = EINA_SLICE_STR_LITERAL("");
ecore_init();
args = ecore_getopt_parse(&options, values, argc, argv);
if (args < 0)
{
fputs("ERROR: Could not parse command line options.\n", stderr);
retval = EXIT_FAILURE;
goto end;
}
if (quit_option) goto end;
args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
if (args < 0)
{
fputs("ERROR: Could not parse positional arguments.\n", stderr);
retval = EXIT_FAILURE;
goto end;
}
/* Efl_Io_Copier works with any object that implements
* Efl_Io_Reader and Efl_Io_Writer interfaces. Here we create
* couple of different objects to showcase that.
*
* Note that input_cbs(), output_cbs() are OPTIONAL, here are only
* used to print out how each object behaves.
*/
if (strcmp(input_fname, ":stdin:") == 0)
{
input = efl_add(EFL_IO_STDIN_CLASS, NULL,
efl_event_callback_array_add(efl_self, input_cbs(), NULL));
if (!input)
{
fprintf(stderr, "ERROR: could not open stdin.\n");
retval = EXIT_FAILURE;
goto end;
}
}
else
{
/* regular file, open with flags: read-only and close-on-exec */
input = efl_add(EFL_IO_FILE_CLASS, NULL,
efl_file_set(efl_self, input_fname, NULL), /* mandatory */
efl_io_file_flags_set(efl_self, O_RDONLY | O_CLOEXEC), /* recommended */
efl_event_callback_array_add(efl_self, input_cbs(), NULL) /* optional */
);
if (!input)
{
fprintf(stderr, "ERROR: could not open '%s' for read.\n",
input_fname);
retval = EXIT_FAILURE;
goto end;
}
}
if (strcmp(output_fname, ":stdout:") == 0)
{
output = efl_add(EFL_IO_STDOUT_CLASS, NULL,
efl_event_callback_array_add(efl_self, output_cbs(), NULL) /* optional */
);
if (!output)
{
fprintf(stderr, "ERROR: could not open stdout.\n");
retval = EXIT_FAILURE;
goto end_input;
}
}
else if (strcmp(output_fname, ":stderr:") == 0)
{
output = efl_add(EFL_IO_STDERR_CLASS, NULL,
efl_event_callback_array_add(efl_self, output_cbs(), NULL) /* optional */
);
if (!output)
{
fprintf(stderr, "ERROR: could not open stderr.\n");
retval = EXIT_FAILURE;
goto end_input;
}
}
else if (strcmp(output_fname, ":memory:") == 0)
{
/*
* This uses a memory destination, then the process will
* result in that object holding all the memory.
*
* One can use things like efl_io_buffer_limit_set() to limit
* it's size.
*
* If the source object provides a size (ie: a file), then
* this buffer will be resized only once at the start, saving
* reallocs. Otherwise it will grow as needed. Contrast this
* with ":none:" method below, that always resize.
*
* When finished get the efl_io_buffer_slice_get(), see
* _copier_done().
*/
output = efl_add(EFL_IO_BUFFER_CLASS, NULL,
efl_event_callback_array_add(efl_self, output_cbs(), NULL), /* optional */
efl_event_callback_array_add(efl_self, output_buffer_cbs(), NULL) /* optional */
);
if (!output)
{
fprintf(stderr, "ERROR: could not open memory buffer.\n");
retval = EXIT_FAILURE;
goto end_input;
}
}
else if (strcmp(output_fname, ":none:") == 0)
{
/*
* No output means that the Efl_Io_Copier will cache
* internally up to efl_io_copier_buffer_limit_set().
*
* When finished you can steal copier's buffer in order to use
* that yourself. See _copier_done().
*/
output = NULL;
}
else
{
/* regular file, open with flags: write-only, close-on-exec,
* create if did not exist and truncate if exist.
*/
output = efl_add(EFL_IO_FILE_CLASS, NULL,
efl_file_set(efl_self, output_fname, NULL), /* mandatory */
efl_io_file_flags_set(efl_self, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC), /* mandatory for write */
efl_io_file_mode_set(efl_self, 0644), /* mandatory for write */
efl_event_callback_array_add(efl_self, output_cbs(), NULL) /* optional */
);
if (!output)
{
fprintf(stderr, "ERROR: could not open '%s' for write.\n",
output_fname);
retval = EXIT_FAILURE;
goto end_input;
}
}
/* A delimiter is optional, if empty or unset, copier will execute
* writes based on read_chunk_size and only event "data" is emitted.
*
* If a line delimiter is set, copier will hold writes until the
* delimiter is found, source reached End-of-Stream (eos) or the
* copier buffer limit is reached. The "line" event is emitted.
*/
if (line_delimiter)
line_delm_slice = (Eina_Slice)EINA_SLICE_STR(line_delimiter);
copier = efl_add(EFL_IO_COPIER_CLASS, ecore_main_loop_get(),
efl_io_copier_source_set(efl_self, input), /* mandatory */
efl_io_copier_destination_set(efl_self, output), /* optional, see :none: */
efl_io_copier_line_delimiter_set(efl_self, &line_delm_slice), /* optional */
efl_io_copier_buffer_limit_set(efl_self, buffer_limit), /* optional, defaults to unlimited */
efl_io_copier_read_chunk_size_set(efl_self, read_chunk_size), /* optional, defaults to 4096 */
efl_event_callback_array_add(efl_self, copier_cbs(), NULL) /* recommended, at least EFL_IO_COPIER_EVENT_DONE. */
);
if (!copier)
{
fprintf(stderr, "ERROR: could not create copier.\n");
retval = EXIT_FAILURE;
goto end_output;
}
fprintf(stderr, "INFO: copy source=%p (%s) to destination=%p (%s)\n",
input,
efl_class_name_get(efl_class_get(input)),
output,
output ? efl_class_name_get(efl_class_get(output)) : ":none:");
ecore_main_loop_begin();
efl_io_closer_close(copier);
efl_del(copier);
copier = NULL;
end_output:
if (output)
{
efl_unref(output);
output = NULL;
}
end_input:
efl_unref(input);
input = NULL;
end:
ecore_shutdown();
return retval;
}

View File

@ -61,6 +61,28 @@ EAPI Eo *ecore_main_loop_get(void);
* @}
*/
/**
* @ingroup Ecore_Fd_Io_Group
*
* @{
*/
#include "efl_io_closer_fd.eo.h"
#include "efl_io_positioner_fd.eo.h"
#include "efl_io_reader_fd.eo.h"
#include "efl_io_sizer_fd.eo.h"
#include "efl_io_writer_fd.eo.h"
#include "efl_io_stdin.eo.h"
#include "efl_io_stdout.eo.h"
#include "efl_io_stderr.eo.h"
#include "efl_io_file.eo.h"
#include "efl_io_copier.eo.h"
/**
* @}
*/
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,49 @@
#define EFL_IO_CLOSER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_CLOSER_FD_CLASS
typedef struct _Efl_Io_Closer_Fd_Data
{
int fd;
} Efl_Io_Closer_Fd_Data;
EOLIAN static void
_efl_io_closer_fd_closer_fd_set(Eo *o EINA_UNUSED, Efl_Io_Closer_Fd_Data *pd, int fd)
{
pd->fd = fd;
}
EOLIAN static int
_efl_io_closer_fd_closer_fd_get(Eo *o EINA_UNUSED, Efl_Io_Closer_Fd_Data *pd)
{
return pd->fd;
}
EOLIAN static Eina_Error
_efl_io_closer_fd_efl_io_closer_close(Eo *o, Efl_Io_Closer_Fd_Data *pd EINA_UNUSED)
{
int fd = efl_io_closer_fd_closer_fd_get(o);
Eina_Error err = 0;
EINA_SAFETY_ON_TRUE_RETURN_VAL(fd < 0, EBADF);
efl_io_closer_fd_closer_fd_set(o, -1);
if (close(fd) < 0) err = errno;
efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
return err;
}
EOLIAN static Eina_Bool
_efl_io_closer_fd_efl_io_closer_closed_get(Eo *o, Efl_Io_Closer_Fd_Data *pd EINA_UNUSED)
{
return efl_io_closer_fd_closer_fd_get(o) < 0;
}
#include "efl_io_closer_fd.eo.c"

View File

@ -0,0 +1,21 @@
mixin Efl.Io.Closer.Fd (Efl.Io.Closer) {
[[Close fd using close(2).
@since 1.19
]]
methods {
@property closer_fd {
get {}
set @protected {}
values {
fd: int;
}
}
}
implements {
Efl.Io.Closer.close;
Efl.Io.Closer.closed.get;
}
}

View File

@ -0,0 +1,712 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_COPIER_CLASS
#define DEF_READ_CHUNK_SIZE 4096
typedef struct _Efl_Io_Copier_Data
{
Efl_Io_Reader *source;
Efl_Io_Writer *destination;
Eina_Promise *job;
Eina_Binbuf *buf;
uint8_t *read_chunk; /* TODO: method to grow Eina_Binbuf so we can expand it and read directly to that */
Eina_Slice line_delimiter;
size_t buffer_limit;
size_t read_chunk_size;
struct {
uint64_t read, written, total;
} progress;
Eina_Bool closed;
Eina_Bool done;
} Efl_Io_Copier_Data;
static void _efl_io_copier_write(Eo *o, Efl_Io_Copier_Data *pd);
static void _efl_io_copier_read(Eo *o, Efl_Io_Copier_Data *pd);
#define _COPIER_DBG(o, pd) \
do \
{ \
if (eina_log_domain_level_check(_ecore_log_dom, EINA_LOG_LEVEL_DBG)) \
{ \
DBG("copier={%p %s, refs=%d, closed=%d, done=%d, buf=%zd}", \
o, \
efl_class_name_get(efl_class_get(o)), \
efl_ref_get(o), \
efl_io_closer_closed_get(o), \
pd->done, \
pd->buf ? eina_binbuf_length_get(pd->buf): 0); \
if (!pd->source) \
DBG("source=NULL"); \
else \
DBG("source={%p %s, refs=%d, can_read=%d, eos=%d, closed=%d}", \
pd->source, \
efl_class_name_get(efl_class_get(pd->source)), \
efl_ref_get(pd->source), \
efl_io_reader_can_read_get(pd->source), \
efl_io_reader_eos_get(pd->source), \
efl_isa(pd->source, EFL_IO_CLOSER_MIXIN) ? \
efl_io_closer_closed_get(pd->source) : 0); \
if (!pd->destination) \
DBG("destination=NULL"); \
else \
DBG("destination={%p %s, refs=%d, can_write=%d, closed=%d}", \
pd->destination, \
efl_class_name_get(efl_class_get(pd->destination)), \
efl_ref_get(pd->destination), \
efl_io_writer_can_write_get(pd->destination), \
efl_isa(pd->destination, EFL_IO_CLOSER_MIXIN) ? \
efl_io_closer_closed_get(pd->destination) : 0); \
} \
} \
while (0)
static void
_efl_io_copier_job(void *data, void *value EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
pd->job = NULL;
_COPIER_DBG(o, pd);
if (pd->source && efl_io_reader_can_read_get(pd->source))
_efl_io_copier_read(o, pd);
if (pd->destination && efl_io_writer_can_write_get(pd->destination))
_efl_io_copier_write(o, pd);
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_PROGRESS, NULL);
if (!pd->source || efl_io_reader_eos_get(pd->source))
{
if ((!pd->done) &&
((!pd->destination) || (eina_binbuf_length_get(pd->buf) == 0)))
{
pd->done = EINA_TRUE;
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_DONE, NULL);
}
}
}
static void
_efl_io_copier_job_schedule(Eo *o, Efl_Io_Copier_Data *pd)
{
if (pd->job) return;
pd->job = efl_loop_job(efl_loop_user_loop_get(o), o);
eina_promise_then(pd->job, _efl_io_copier_job, NULL, o);
}
/* NOTE: the returned slice may be smaller than requested since the
* internal binbuf may be modified from inside event calls.
*
* parameter slice_of_binbuf must have mem pointing to pd->binbuf
*/
static Eina_Slice
_efl_io_copier_dispatch_data_events(Eo *o, Efl_Io_Copier_Data *pd, Eina_Slice slice_of_binbuf)
{
Eina_Slice tmp;
size_t offset;
tmp = eina_binbuf_slice_get(pd->buf);
if ((slice_of_binbuf.bytes < tmp.bytes) ||
(eina_slice_end_get(slice_of_binbuf) > eina_slice_end_get(tmp)))
{
CRI("slice_of_binbuf=" EINA_SLICE_FMT " must be inside binbuf=" EINA_SLICE_FMT,
EINA_SLICE_PRINT(slice_of_binbuf), EINA_SLICE_PRINT(tmp));
return (Eina_Slice){.mem = NULL, .len = 0};
}
offset = slice_of_binbuf.bytes - tmp.bytes;
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_DATA, &slice_of_binbuf);
/* user may have modified pd->buf, like calling
* efl_io_copier_buffer_limit_set()
*/
tmp = eina_binbuf_slice_get(pd->buf);
if (offset <= tmp.len)
{
tmp.len -= offset;
tmp.bytes += offset;
}
if (tmp.len > slice_of_binbuf.len)
tmp.len = slice_of_binbuf.len;
slice_of_binbuf = tmp;
if (pd->line_delimiter.len > 0)
{
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_LINE, &slice_of_binbuf);
/* user may have modified pd->buf, like calling
* efl_io_copier_buffer_limit_set()
*/
tmp = eina_binbuf_slice_get(pd->buf);
if (offset <= tmp.len)
{
tmp.len -= offset;
tmp.bytes += offset;
}
if (tmp.len > slice_of_binbuf.len)
tmp.len = slice_of_binbuf.len;
slice_of_binbuf = tmp;
}
return slice_of_binbuf;
}
static void
_efl_io_copier_read(Eo *o, Efl_Io_Copier_Data *pd)
{
Eina_Rw_Slice rw_slice;
Eina_Slice ro_slice;
Eina_Error err;
size_t used;
EINA_SAFETY_ON_TRUE_RETURN(pd->closed);
rw_slice.mem = pd->read_chunk;
rw_slice.len = pd->read_chunk_size;
used = eina_binbuf_length_get(pd->buf);
if (pd->buffer_limit > 0)
{
if (pd->buffer_limit <= used)
{
// TODO: disconnect 'read' so stops calling?
return;
}
else if (pd->buffer_limit > used)
{
size_t available = pd->buffer_limit - used;
if (rw_slice.len > available)
rw_slice.len = available;
}
}
err = efl_io_reader_read(pd->source, &rw_slice);
if (err)
{
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_ERROR, &err);
return;
}
ro_slice = eina_rw_slice_slice_get(rw_slice);
if (!eina_binbuf_append_slice(pd->buf, ro_slice))
{
err = ENOMEM;
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_ERROR, &err);
return;
}
pd->progress.read += rw_slice.len;
pd->done = EINA_FALSE;
if (!pd->destination)
{
/* Note: if there is a destination, dispatch data and line
* from write since it will remove from binbuf and make it
* simple to not repeat data that was already sent.
*
* however, if there is no destination, then emit the event
* here.
*
* Remember to get the actual binbuf memory, rw_slice/ro_slice
* contains the pointer to pd->read_chunk and
* _efl_io_copier_dispatch_data_events() needs a slice to
* internal binbuf.
*/
Eina_Slice binbuf_slice = eina_binbuf_slice_get(pd->buf);
Eina_Slice ev_slice = {
.mem = binbuf_slice.bytes + used,
.len = binbuf_slice.len - used,
};
_efl_io_copier_dispatch_data_events(o, pd, ev_slice);
}
_efl_io_copier_job_schedule(o, pd);
}
static void
_efl_io_copier_write(Eo *o, Efl_Io_Copier_Data *pd)
{
Eina_Slice ro_slice = eina_binbuf_slice_get(pd->buf);
Eina_Error err;
EINA_SAFETY_ON_TRUE_RETURN(pd->closed);
if (ro_slice.len == 0)
{
// TODO: disconnect 'write' so stops calling?
return;
}
if ((pd->line_delimiter.len > 0) &&
(pd->source && !efl_io_reader_eos_get(pd->source)))
{
const uint8_t *p = eina_slice_find(ro_slice, pd->line_delimiter);
if (p)
ro_slice.len = p - ro_slice.bytes + pd->line_delimiter.len;
else if ((pd->buffer_limit == 0) || (ro_slice.len < pd->buffer_limit))
{
// TODO: disconnect 'write' so stops calling?
return;
}
}
err = efl_io_writer_write(pd->destination, &ro_slice, NULL);
if (err)
{
if (err != EAGAIN)
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_ERROR, &err);
return;
}
pd->progress.written += ro_slice.len;
pd->done = EINA_FALSE;
/* Note: dispatch data and line from write since it will remove
* from binbuf and make it simple to not repeat data that was
* already sent.
*/
ro_slice = _efl_io_copier_dispatch_data_events(o, pd, ro_slice);
if (!eina_binbuf_remove(pd->buf, 0, ro_slice.len))
{
err = ENOMEM;
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_ERROR, &err);
return;
}
_efl_io_copier_job_schedule(o, pd);
}
static void
_efl_io_copier_source_can_read_changed(void *data, const Eo_Event *event EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
if (pd->closed) return;
_COPIER_DBG(o, pd);
if (efl_io_reader_can_read_get(pd->source))
_efl_io_copier_job_schedule(o, pd);
}
static void
_efl_io_copier_source_eos(void *data, const Eo_Event *event EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
if (pd->closed) return;
_COPIER_DBG(o, pd);
_efl_io_copier_job_schedule(o, pd);
}
static void
_efl_io_copier_source_size_apply(Eo *o, Efl_Io_Copier_Data *pd)
{
if (pd->closed) return;
pd->progress.total = efl_io_sizer_size_get(pd->source);
_COPIER_DBG(o, pd);
if (pd->destination && efl_isa(pd->destination, EFL_IO_SIZER_MIXIN))
efl_io_sizer_resize(pd->destination, pd->progress.total);
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_PROGRESS, NULL);
}
static void
_efl_io_copier_source_resized(void *data, const Eo_Event *event EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
_efl_io_copier_source_size_apply(o, pd);
}
static void
_efl_io_copier_source_closed(void *data, const Eo_Event *event EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
if (pd->closed) return;
_COPIER_DBG(o, pd);
_efl_io_copier_job_schedule(o, pd);
}
EFL_CALLBACKS_ARRAY_DEFINE(source_cbs,
{ EFL_IO_READER_EVENT_CAN_READ_CHANGED, _efl_io_copier_source_can_read_changed },
{ EFL_IO_READER_EVENT_EOS, _efl_io_copier_source_eos });
EOLIAN static Efl_Io_Reader *
_efl_io_copier_source_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
return pd->source;
}
EOLIAN static void
_efl_io_copier_source_set(Eo *o, Efl_Io_Copier_Data *pd, Efl_Io_Reader *source)
{
if (pd->source == source) return;
if (pd->source)
{
if (efl_isa(pd->source, EFL_IO_SIZER_MIXIN))
{
efl_event_callback_del(pd->source, EFL_IO_SIZER_EVENT_SIZE_CHANGED,
_efl_io_copier_source_resized, o);
pd->progress.total = 0;
}
if (efl_isa(pd->source, EFL_IO_CLOSER_MIXIN))
{
efl_event_callback_del(pd->source, EFL_IO_CLOSER_EVENT_CLOSED,
_efl_io_copier_source_closed, o);
}
efl_event_callback_array_del(pd->source, source_cbs(), o);
efl_unref(pd->source);
pd->source = NULL;
}
if (source)
{
EINA_SAFETY_ON_TRUE_RETURN(pd->closed);
pd->source = efl_ref(source);
efl_event_callback_array_add(pd->source, source_cbs(), o);
if (efl_isa(pd->source, EFL_IO_SIZER_MIXIN))
{
efl_event_callback_add(pd->source, EFL_IO_SIZER_EVENT_SIZE_CHANGED,
_efl_io_copier_source_resized, o);
_efl_io_copier_source_size_apply(o, pd);
}
if (efl_isa(pd->source, EFL_IO_CLOSER_MIXIN))
{
efl_event_callback_add(pd->source, EFL_IO_CLOSER_EVENT_CLOSED,
_efl_io_copier_source_closed, o);
}
}
}
static void
_efl_io_copier_destination_can_write_changed(void *data, const Eo_Event *event EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
if (pd->closed) return;
_COPIER_DBG(o, pd);
if (efl_io_writer_can_write_get(pd->destination))
_efl_io_copier_job_schedule(o, pd);
}
static void
_efl_io_copier_destination_closed(void *data, const Eo_Event *event EINA_UNUSED)
{
Eo *o = data;
Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS);
if (pd->closed) return;
_COPIER_DBG(o, pd);
if (eina_binbuf_length_get(pd->buf) == 0)
{
if (!pd->done)
{
pd->done = EINA_TRUE;
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_DONE, NULL);
}
}
else
{
Eina_Error err = EBADF;
efl_event_callback_call(o, EFL_IO_COPIER_EVENT_ERROR, &err);
}
}
EFL_CALLBACKS_ARRAY_DEFINE(destination_cbs,
{ EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _efl_io_copier_destination_can_write_changed });
EOLIAN static Efl_Io_Writer *
_efl_io_copier_destination_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
return pd->destination;
}
EOLIAN static void
_efl_io_copier_destination_set(Eo *o, Efl_Io_Copier_Data *pd, Efl_Io_Writer *destination)
{
if (pd->destination == destination) return;
if (pd->destination)
{
efl_event_callback_array_del(pd->destination, destination_cbs(), o);
if (efl_isa(pd->destination, EFL_IO_CLOSER_MIXIN))
{
efl_event_callback_del(pd->destination, EFL_IO_CLOSER_EVENT_CLOSED,
_efl_io_copier_destination_closed, o);
}
efl_unref(pd->destination);
pd->destination = NULL;
}
if (destination)
{
EINA_SAFETY_ON_TRUE_RETURN(pd->closed);
pd->destination = efl_ref(destination);
efl_event_callback_array_add(pd->destination, destination_cbs(), o);
if (efl_isa(pd->destination, EFL_IO_CLOSER_MIXIN))
{
efl_event_callback_add(pd->destination, EFL_IO_CLOSER_EVENT_CLOSED,
_efl_io_copier_destination_closed, o);
}
if (efl_isa(pd->destination, EFL_IO_SIZER_MIXIN) &&
pd->source && efl_isa(pd->source, EFL_IO_SIZER_MIXIN))
{
efl_io_sizer_resize(pd->destination, pd->progress.total);
}
}
}
EOLIAN static void
_efl_io_copier_buffer_limit_set(Eo *o, Efl_Io_Copier_Data *pd, size_t size)
{
size_t used;
EINA_SAFETY_ON_TRUE_RETURN(pd->closed);
if (pd->buffer_limit == size) return;
pd->buffer_limit = size;
if (size == 0) return;
used = eina_binbuf_length_get(pd->buf);
if (used > size) eina_binbuf_remove(pd->buf, size, used);
if (pd->read_chunk_size > size) efl_io_copier_read_chunk_size_set(o, size);
}
EOLIAN static size_t
_efl_io_copier_buffer_limit_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
return pd->buffer_limit;
}
EOLIAN static void
_efl_io_copier_line_delimiter_set(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd, const Eina_Slice *slice)
{
EINA_SAFETY_ON_NULL_RETURN(slice);
if (pd->line_delimiter.mem == slice->mem)
{
pd->line_delimiter.len = slice->len;
return;
}
free((void *)pd->line_delimiter.mem);
if (slice->len == 0)
{
pd->line_delimiter.mem = NULL;
pd->line_delimiter.len = 0;
}
else
{
Eina_Rw_Slice rw_slice = eina_slice_dup(*slice);
pd->line_delimiter = eina_rw_slice_slice_get(rw_slice);
}
}
EOLIAN static const Eina_Slice *
_efl_io_copier_line_delimiter_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
return &pd->line_delimiter;
}
EOLIAN static void
_efl_io_copier_read_chunk_size_set(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd, size_t size)
{
void *tmp;
EINA_SAFETY_ON_TRUE_RETURN(pd->closed);
if (size == 0) size = DEF_READ_CHUNK_SIZE;
if ((pd->read_chunk_size == size) && pd->read_chunk) return;
tmp = realloc(pd->read_chunk, size);
EINA_SAFETY_ON_NULL_RETURN(tmp);
pd->read_chunk = tmp;
pd->read_chunk_size = size;
}
EOLIAN static size_t
_efl_io_copier_read_chunk_size_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
return pd->read_chunk_size > 0 ? pd->read_chunk_size : DEF_READ_CHUNK_SIZE;
}
EOLIAN static Eina_Error
_efl_io_copier_efl_io_closer_close(Eo *o, Efl_Io_Copier_Data *pd)
{
Eina_Error err = 0, r;
EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->closed, EINVAL);
_COPIER_DBG(o, pd);
if (pd->job)
{
eina_promise_cancel(pd->job);
pd->job = NULL;
}
if (pd->source)
{
if (efl_isa(pd->source, EFL_IO_SIZER_MIXIN))
{
efl_event_callback_del(pd->source, EFL_IO_SIZER_EVENT_SIZE_CHANGED,
_efl_io_copier_source_resized, o);
pd->progress.total = 0;
}
efl_event_callback_array_del(pd->source, source_cbs(), o);
if (efl_isa(pd->source, EFL_IO_CLOSER_MIXIN) &&
!efl_io_closer_closed_get(pd->source))
{
efl_event_callback_del(pd->source, EFL_IO_CLOSER_EVENT_CLOSED,
_efl_io_copier_source_closed, o);
err = efl_io_closer_close(pd->source);
}
}
if (pd->destination)
{
efl_event_callback_array_del(pd->destination, destination_cbs(), o);
if (efl_isa(pd->destination, EFL_IO_CLOSER_MIXIN) &&
!efl_io_closer_closed_get(pd->destination))
{
efl_event_callback_del(pd->destination, EFL_IO_CLOSER_EVENT_CLOSED,
_efl_io_copier_destination_closed, o);
r = efl_io_closer_close(pd->destination);
if (!err) err = r;
}
}
pd->closed = EINA_TRUE;
efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
if (pd->buf)
{
eina_binbuf_free(pd->buf);
pd->buf = NULL;
}
if (pd->read_chunk)
{
free(pd->read_chunk);
pd->read_chunk = NULL;
pd->read_chunk_size = 0;
}
return err;
}
EOLIAN static Eina_Bool
_efl_io_copier_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
return pd->closed;
}
EOLIAN static void
_efl_io_copier_progress_get(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd, uint64_t *read, uint64_t *written, uint64_t *total)
{
if (read) *read = pd->progress.read;
if (written) *written = pd->progress.written;
if (total) *total = pd->progress.total;
}
EOLIAN static Eina_Binbuf *
_efl_io_copier_binbuf_steal(Eo *o EINA_UNUSED, Efl_Io_Copier_Data *pd)
{
Eina_Binbuf *ret = pd->buf;
pd->buf = eina_binbuf_new();
return ret;
}
EOLIAN static Eo *
_efl_io_copier_efl_object_constructor(Eo *o, Efl_Io_Copier_Data *pd)
{
pd->buf = eina_binbuf_new();
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->buf, NULL);
return efl_constructor(efl_super(o, MY_CLASS));
}
EOLIAN static Eo *
_efl_io_copier_efl_object_finalize(Eo *o, Efl_Io_Copier_Data *pd)
{
if (pd->read_chunk_size == 0)
efl_io_copier_read_chunk_size_set(o, DEF_READ_CHUNK_SIZE);
if (!efl_loop_user_loop_get(o))
{
ERR("Set a loop provider as parent of this copier!");
return NULL;
}
if ((pd->source && efl_io_reader_can_read_get(pd->source)) ||
(pd->destination && efl_io_writer_can_write_get(pd->destination)))
_efl_io_copier_job_schedule(o, pd);
_COPIER_DBG(o, pd);
return efl_finalize(efl_super(o, MY_CLASS));
}
EOLIAN static void
_efl_io_copier_efl_object_destructor(Eo *o, Efl_Io_Copier_Data *pd)
{
_COPIER_DBG(o, pd);
efl_io_copier_source_set(o, NULL);
efl_io_copier_destination_set(o, NULL);
if (pd->job)
{
eina_promise_cancel(pd->job);
pd->job = NULL;
}
efl_destructor(efl_super(o, MY_CLASS));
if (pd->buf)
{
eina_binbuf_free(pd->buf);
pd->buf = NULL;
}
if (pd->read_chunk)
{
free(pd->read_chunk);
pd->read_chunk = NULL;
pd->read_chunk_size = 0;
}
if (pd->line_delimiter.mem)
{
free((void *)pd->line_delimiter.mem);
pd->line_delimiter.mem = NULL;
pd->line_delimiter.len = 0;
}
}
#include "efl_io_copier.eo.c"

View File

@ -0,0 +1,112 @@
import eina_types;
class Efl.Io.Copier (Efl.Loop_User, Efl.Io.Closer) {
[[Copy from an @Efl.Io.Reader source to @Efl.Io.Writer destination.
During usage it will keep reference to @.source and
@.destination objects, automatically relasing them on
destructor.
By default the read-write process is done based on fixed-size
chunks (@.read_chunk_size), however if @.line_delimiter is set,
the behavior changes to wait for such delimiter or a maximum
buffer limit is reached (@.buffer_limit).
If @Efl.Io.Closer.close is called, then it will be called on
@.source and @.destination if they implement those interfaces.
@since 1.19
]]
methods {
@property source {
get {
}
set {
[[Constructor-only property to set where to read data from]]
}
values {
source: Efl.Io.Reader;
}
}
@property destination {
get {
}
set {
[[Constructor-only property to set where to write data to]]
}
values {
destination: Efl.Io.Writer;
}
}
@property line_delimiter {
[[If there is a line delimiter, the reads will buffer/queue up to the line delimiter before calling @Efl.Io.Writer.write on the @.destination and the event line is emitted with current line. The line may include the delimiter, unless it's end-of-stream on @.source or @.buffer_limit was reached.]]
get { }
set {
[[Change line delimiter to use. If NULL or empty, no delimiter is to be used]]
}
values {
slice: const(Eina.Slice)*; [[The contents may contain \0 and will be copied]]
}
}
@property buffer_limit {
get {
}
set {
[[Constructor-only property to set buffer limit. 0 is unlimited]]
}
values {
size: size; [[Defines a maximum buffer limit, or 0 to allow unlimited amount of bytes]]
}
}
@property read_chunk_size {
get {
}
set {
[[Set chunk size for each basic @Efl.Io.Reader.read operation.]]
}
values {
size: size; [[This is the chunk size to use for read operations]]
}
}
@property progress {
get {
}
values {
read: uint64 @optional; [[amount of bytes read from source]]
written: uint64 @optional; [[amount of bytes written to destination]]
total: uint64 @optional; [[If @.source is an Efl.Io.Sizer, its total size. Otherwise 0 to report unknown size]]
}
}
binbuf_steal {
[[Steals the internal binbuf and return it to caller.
The buffer is then owned by caller, which should call
eina_binbuf_free() when it's done.
]]
return: free(own(Eina.Binbuf*), eina_binbuf_free) @warn_unused;
}
}
events {
done; [[All available data was copied from source to destination]]
error: Eina.Error; [[An error happened and the copy stopped]]
progress; [[Total size changed or Data was read/written]]
data: const(Eina.Slice)*; [[When data is read to internal buffer, it's emitted in this event. The memory is only valid during event callback dispatched and should not be modified.]]
line: 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.]]
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.finalize;
Efl.Io.Closer.close;
Efl.Io.Closer.closed.get;
}
}

195
src/lib/ecore/efl_io_file.c Normal file
View File

@ -0,0 +1,195 @@
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_READER_FD_PROTECTED 1
#define EFL_IO_WRITER_FD_PROTECTED 1
#define EFL_IO_CLOSER_FD_PROTECTED 1
#define EFL_IO_SIZER_FD_PROTECTED 1
#define EFL_IO_POSITIONER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MY_CLASS EFL_IO_FILE_CLASS
typedef struct _Efl_Io_File_Data
{
const char *path;
uint32_t flags;
uint32_t mode;
uint64_t last_position;
// TODO: monitor reader.can_read,changed/writer.can_write,changed events in order to dynamically connect to Loop_Fd events.
} Efl_Io_File_Data;
static void
_efl_io_file_state_update(Eo *o, Efl_Io_File_Data *pd)
{
uint64_t pos = efl_io_positioner_position_get(o);
uint64_t size = efl_io_sizer_size_get(o);
uint32_t flags = pd->flags & O_ACCMODE;
if ((flags == O_RDWR) || (flags == O_RDONLY))
{
efl_io_reader_can_read_set(o, pos < size);
efl_io_reader_eos_set(o, pos >= size);
}
if ((flags == O_RDWR) || (flags == O_WRONLY))
efl_io_writer_can_write_set(o, EINA_TRUE);
if (pd->last_position != pos)
{
pd->last_position = pos;
efl_event_callback_call(o, EFL_IO_POSITIONER_EVENT_POSITION_CHANGED, NULL);
}
}
EOLIAN static void
_efl_io_file_efl_loop_fd_fd_file_set(Eo *o, Efl_Io_File_Data *pd, int fd)
{
efl_loop_fd_file_set(efl_super(o, MY_CLASS), fd);
efl_io_positioner_fd_positioner_fd_set(o, fd);
efl_io_sizer_fd_sizer_fd_set(o, fd);
efl_io_reader_fd_reader_fd_set(o, fd);
efl_io_writer_fd_writer_fd_set(o, fd);
efl_io_closer_fd_closer_fd_set(o, fd);
if (fd >= 0) _efl_io_file_state_update(o, pd);
}
EOLIAN static void
_efl_io_file_flags_set(Eo *o, Efl_Io_File_Data *pd, uint32_t flags)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_finalized_get(o));
pd->flags = flags;
}
EOLIAN static uint32_t
_efl_io_file_flags_get(Eo *o EINA_UNUSED, Efl_Io_File_Data *pd)
{
return pd->flags; // TODO: query from fd?
}
EOLIAN static void
_efl_io_file_mode_set(Eo *o, Efl_Io_File_Data *pd, uint32_t mode)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_finalized_get(o));
pd->mode = mode;
}
EOLIAN static uint32_t
_efl_io_file_mode_get(Eo *o EINA_UNUSED, Efl_Io_File_Data *pd)
{
return pd->mode;
}
EOLIAN static void
_efl_io_file_efl_object_destructor(Eo *o, Efl_Io_File_Data *pd)
{
if (!efl_io_closer_closed_get(o))
efl_io_closer_close(o);
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_del(pd->path);
}
EOLIAN static Efl_Object *
_efl_io_file_efl_object_finalize(Eo *o, Efl_Io_File_Data *pd)
{
int fd = efl_loop_fd_file_get(o);
if (fd < 0)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->path, NULL);
if (pd->mode)
fd = open(pd->path, pd->flags, pd->mode);
else
fd = open(pd->path, pd->flags);
if (fd < 0)
{
eina_error_set(errno);
ERR("Could not open file '%s': %s", pd->path, strerror(errno));
return NULL;
}
efl_loop_fd_file_set(o, fd);
}
return efl_finalize(efl_super(o, MY_CLASS));
}
EOLIAN static Eina_Bool
_efl_io_file_efl_file_file_set(Eo *o, Efl_Io_File_Data *pd, const char *file, const char *key EINA_UNUSED)
{
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_finalized_get(o), EINA_FALSE);
eina_stringshare_replace(&pd->path, file);
return EINA_TRUE;
}
EOLIAN static void
_efl_io_file_efl_file_file_get(Eo *o EINA_UNUSED, Efl_Io_File_Data *pd, const char **file, const char **key)
{
if (file) *file = pd->path;
if (key) *key = NULL;
}
EOLIAN static Eina_Error
_efl_io_file_efl_io_reader_read(Eo *o, Efl_Io_File_Data *pd, Eina_Rw_Slice *rw_slice)
{
Eina_Error err = efl_io_reader_read(efl_super(o, MY_CLASS), rw_slice);
if (err) return err;
_efl_io_file_state_update(o, pd);
return 0;
}
EOLIAN static Eina_Error
_efl_io_file_efl_io_writer_write(Eo *o, Efl_Io_File_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
{
Eina_Error err = efl_io_writer_write(efl_super(o, MY_CLASS), slice, remaining);
if (err) return err;
_efl_io_file_state_update(o, pd);
return 0;
}
EOLIAN static Eina_Error
_efl_io_file_efl_io_closer_close(Eo *o, Efl_Io_File_Data *pd EINA_UNUSED)
{
Eina_Error ret;
efl_io_reader_can_read_set(o, EINA_FALSE);
efl_io_reader_eos_set(o, EINA_TRUE);
efl_io_writer_can_write_set(o, EINA_FALSE);
ret = efl_io_closer_close(efl_super(o, MY_CLASS));
efl_loop_fd_file_set(o, -1);
return ret;
}
EOLIAN static Eina_Error
_efl_io_file_efl_io_sizer_resize(Eo *o, Efl_Io_File_Data *pd, uint64_t size)
{
Eina_Error err = efl_io_sizer_resize(efl_super(o, MY_CLASS), size);
if (err) return err;
_efl_io_file_state_update(o, pd);
return 0;
}
EOLIAN static Eina_Error
_efl_io_file_efl_io_positioner_seek(Eo *o, Efl_Io_File_Data *pd, int64_t offset, Efl_Io_Positioner_Whence whence)
{
Eina_Error err = efl_io_positioner_seek(efl_super(o, MY_CLASS), offset, whence);
if (err) return err;
_efl_io_file_state_update(o, pd);
return 0;
}
#include "efl_io_file.eo.c"

View File

@ -0,0 +1,49 @@
class Efl.Io.File (Efl.Loop.Fd, Efl.File, Efl.Io.Reader.Fd, Efl.Io.Writer.Fd, Efl.Io.Closer.Fd, Efl.Io.Sizer.Fd, Efl.Io.Positioner.Fd) {
[[File access (open, close, read, write, lseek, ftruncate)
Files are closed automatically (@Efl.Io.Closer.Fd) on destruction.
@since 1.19
]]
methods {
@property flags {
[[bitwise OR'ed flags to open the file, like O_CREAT, O_CLOEXEC...]]
get {
}
set {
[[constructor property to define flags to open the file]]
}
values {
flags: uint32; [[flags to open file, see man:open(2).]]
}
}
@property mode {
get {
}
set {
[[constructor property to define mode to open the file]]
}
values {
mode: uint32; [[mode to open file, see man:open(2).]]
}
}
}
implements {
Efl.Object.destructor;
Efl.Object.finalize;
Efl.Loop.Fd.fd_file.set;
Efl.File.file;
Efl.Io.Reader.read;
Efl.Io.Writer.write;
Efl.Io.Closer.close;
Efl.Io.Sizer.resize;
Efl.Io.Positioner.seek;
}
}

View File

@ -0,0 +1,65 @@
#define EFL_IO_POSITIONER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_POSITIONER_FD_CLASS
typedef struct _Efl_Io_Positioner_Fd_Data
{
int fd;
} Efl_Io_Positioner_Fd_Data;
EOLIAN static void
_efl_io_positioner_fd_positioner_fd_set(Eo *o EINA_UNUSED, Efl_Io_Positioner_Fd_Data *pd, int fd)
{
pd->fd = fd;
}
EOLIAN static int
_efl_io_positioner_fd_positioner_fd_get(Eo *o EINA_UNUSED, Efl_Io_Positioner_Fd_Data *pd)
{
return pd->fd;
}
static inline int
_efl_io_positioner_whence_convert(Efl_Io_Positioner_Whence whence)
{
switch (whence)
{
case EFL_IO_POSITIONER_WHENCE_START: return SEEK_SET;
case EFL_IO_POSITIONER_WHENCE_CURRENT: return SEEK_CUR;
case EFL_IO_POSITIONER_WHENCE_END: return SEEK_END;
}
return SEEK_SET;
}
EOLIAN static Eina_Error
_efl_io_positioner_fd_efl_io_positioner_seek(Eo *o, Efl_Io_Positioner_Fd_Data *pd EINA_UNUSED, int64_t offset, Efl_Io_Positioner_Whence whence)
{
int fd = efl_io_positioner_fd_positioner_fd_get(o);
if (lseek(fd, (off_t)offset, _efl_io_positioner_whence_convert(whence)) < 0)
return errno;
efl_event_callback_call(o, EFL_IO_POSITIONER_EVENT_POSITION_CHANGED, NULL);
return 0;
}
EOLIAN static uint64_t
_efl_io_positioner_fd_efl_io_positioner_position_get(Eo *o, Efl_Io_Positioner_Fd_Data *pd EINA_UNUSED)
{
int fd = efl_io_positioner_fd_positioner_fd_get(o);
off_t offset;
EINA_SAFETY_ON_TRUE_RETURN_VAL(fd < 0, 0);
offset = lseek(fd, 0, SEEK_CUR);
EINA_SAFETY_ON_TRUE_RETURN_VAL(offset < 0, 0);
return offset;
}
#include "efl_io_positioner_fd.eo.c"

View File

@ -0,0 +1,21 @@
mixin Efl.Io.Positioner.Fd (Efl.Io.Positioner) {
[[Position fd using lseek(2).
@since 1.19
]]
methods {
@property positioner_fd {
get {}
set @protected {}
values {
fd: int;
}
}
}
implements {
Efl.Io.Positioner.seek;
Efl.Io.Positioner.position.get;
}
}

View File

@ -0,0 +1,100 @@
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_READER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_READER_FD_CLASS
typedef struct _Efl_Io_Reader_Fd_Data
{
int fd;
Eina_Bool can_read;
Eina_Bool eos;
} Efl_Io_Reader_Fd_Data;
EOLIAN static void
_efl_io_reader_fd_reader_fd_set(Eo *o EINA_UNUSED, Efl_Io_Reader_Fd_Data *pd, int fd)
{
pd->fd = fd;
}
EOLIAN static int
_efl_io_reader_fd_reader_fd_get(Eo *o EINA_UNUSED, Efl_Io_Reader_Fd_Data *pd)
{
return pd->fd;
}
EOLIAN static Eina_Error
_efl_io_reader_fd_efl_io_reader_read(Eo *o, Efl_Io_Reader_Fd_Data *pd EINA_UNUSED, Eina_Rw_Slice *rw_slice)
{
int fd = efl_io_reader_fd_reader_fd_get(o);
ssize_t r;
EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
if (fd < 0) goto error;
do
{
r = read(fd, rw_slice->mem, rw_slice->len);
if (r < 0)
{
if (errno == EINTR) continue;
rw_slice->len = 0;
rw_slice->mem = NULL;
if (errno == EAGAIN) efl_io_reader_can_read_set(o, EINA_FALSE);
return errno;
}
}
while (r < 0);
rw_slice->len = r;
if (r == 0)
{
efl_io_reader_can_read_set(o, EINA_FALSE);
efl_io_reader_eos_set(o, EINA_TRUE);
}
return 0;
error:
rw_slice->len = 0;
rw_slice->mem = NULL;
return EINVAL;
}
EOLIAN static Eina_Bool
_efl_io_reader_fd_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Io_Reader_Fd_Data *pd)
{
return pd->can_read;
}
EOLIAN static void
_efl_io_reader_fd_efl_io_reader_can_read_set(Eo *o, Efl_Io_Reader_Fd_Data *pd, Eina_Bool can_read)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_reader_fd_reader_fd_get(o) < 0);
if (pd->can_read == can_read) return;
pd->can_read = can_read;
efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
}
EOLIAN static Eina_Bool
_efl_io_reader_fd_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Io_Reader_Fd_Data *pd)
{
return pd->eos;
}
EOLIAN static void
_efl_io_reader_fd_efl_io_reader_eos_set(Eo *o, Efl_Io_Reader_Fd_Data *pd, Eina_Bool is_eos)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_reader_fd_reader_fd_get(o) < 0);
if (pd->eos == is_eos) return;
pd->eos = is_eos;
if (is_eos)
efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
}
#include "efl_io_reader_fd.eo.c"

View File

@ -0,0 +1,24 @@
mixin Efl.Io.Reader.Fd (Efl.Io.Reader) {
[[Read fd using read(2).
@since 1.19
]]
methods {
@property reader_fd {
get {}
set @protected {}
values {
fd: int;
}
}
}
implements {
Efl.Io.Reader.read;
Efl.Io.Reader.can_read.get;
Efl.Io.Reader.can_read.set;
Efl.Io.Reader.eos.get;
Efl.Io.Reader.eos.set;
}
}

View File

@ -0,0 +1,53 @@
#define EFL_IO_SIZER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_SIZER_FD_CLASS
typedef struct _Efl_Io_Sizer_Fd_Data
{
int fd;
} Efl_Io_Sizer_Fd_Data;
EOLIAN static void
_efl_io_sizer_fd_sizer_fd_set(Eo *o EINA_UNUSED, Efl_Io_Sizer_Fd_Data *pd, int fd)
{
pd->fd = fd;
}
EOLIAN static int
_efl_io_sizer_fd_sizer_fd_get(Eo *o EINA_UNUSED, Efl_Io_Sizer_Fd_Data *pd)
{
return pd->fd;
}
EOLIAN static Eina_Error
_efl_io_sizer_fd_efl_io_sizer_resize(Eo *o, Efl_Io_Sizer_Fd_Data *pd EINA_UNUSED, uint64_t size)
{
int fd = efl_io_sizer_fd_sizer_fd_get(o);
if (ftruncate(fd, size) < 0) return errno;
efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
return 0;
}
EOLIAN static uint64_t
_efl_io_sizer_fd_efl_io_sizer_size_get(Eo *o, Efl_Io_Sizer_Fd_Data *pd EINA_UNUSED)
{
int fd = efl_io_sizer_fd_sizer_fd_get(o);
struct stat st;
int r;
EINA_SAFETY_ON_TRUE_RETURN_VAL(fd < 0, 0);
r = fstat(fd, &st);
EINA_SAFETY_ON_TRUE_RETURN_VAL(r < 0, 0);
return st.st_size;
}
#include "efl_io_sizer_fd.eo.c"

View File

@ -0,0 +1,21 @@
mixin Efl.Io.Sizer.Fd (Efl.Io.Sizer) {
[[Resize fd usign ftruncate(2).
@since 1.19
]]
methods {
@property sizer_fd {
get {}
set @protected {}
values {
fd: int;
}
}
}
implements {
Efl.Io.Sizer.resize;
Efl.Io.Sizer.size.get;
}
}

View File

@ -0,0 +1,60 @@
#define EFL_IO_WRITER_FD_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_STDERR_CLASS
static void
_efl_io_stderr_event_write(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_writer_can_write_set(event->object, EINA_TRUE);
}
static void
_efl_io_stderr_event_error(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_writer_can_write_set(event->object, EINA_FALSE);
}
EOLIAN static void
_efl_io_stderr_efl_loop_fd_fd_set(Eo *o, void *pd EINA_UNUSED, int fd)
{
efl_loop_fd_file_set(efl_super(o, MY_CLASS), fd);
efl_io_writer_fd_writer_fd_set(o, fd);
}
EOLIAN static Efl_Object *
_efl_io_stderr_efl_object_finalize(Eo *o, void *pd EINA_UNUSED)
{
int fd = efl_loop_fd_get(o);
if (fd < 0) efl_loop_fd_set(o, STDIN_FILENO);
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
// TODO: only register "write" if "can_write" is being monitored?
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_WRITE, _efl_io_stderr_event_write, NULL);
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_ERROR, _efl_io_stderr_event_error, NULL);
return o;
}
EOLIAN static Eina_Error
_efl_io_stderr_efl_io_writer_write(Eo *o, void *pd EINA_UNUSED, Eina_Slice *ro_slice, Eina_Slice *remaining)
{
Eina_Error ret;
ret = efl_io_writer_write(efl_super(o, MY_CLASS), ro_slice, remaining);
if (ro_slice && ro_slice->len > 0)
efl_io_writer_can_write_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "write" */
return ret;
}
#include "efl_io_stderr.eo.c"

View File

@ -0,0 +1,14 @@
class Efl.Io.Stderr (Efl.Loop.Fd, Efl.Io.Writer.Fd) {
[[Application's standard error (stderr).
@since 1.19
]]
data: null;
implements {
Efl.Object.finalize;
Efl.Loop.Fd.fd.set;
Efl.Io.Writer.write;
}
}

View File

@ -0,0 +1,61 @@
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_READER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_STDIN_CLASS
static void
_efl_io_stdin_event_read(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_reader_can_read_set(event->object, EINA_TRUE);
efl_io_reader_eos_set(event->object, EINA_FALSE);
}
static void
_efl_io_stdin_event_error(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_reader_can_read_set(event->object, EINA_FALSE);
efl_io_reader_eos_set(event->object, EINA_TRUE);
}
EOLIAN static void
_efl_io_stdin_efl_loop_fd_fd_set(Eo *o, void *pd EINA_UNUSED, int fd)
{
efl_loop_fd_file_set(efl_super(o, MY_CLASS), fd);
efl_io_reader_fd_reader_fd_set(o, fd);
}
EOLIAN static Efl_Object *
_efl_io_stdin_efl_object_finalize(Eo *o, void *pd EINA_UNUSED)
{
int fd = efl_loop_fd_get(o);
if (fd < 0) efl_loop_fd_set(o, STDIN_FILENO);
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
// TODO: only register "read" if "can_read" is being monitored?
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_READ, _efl_io_stdin_event_read, NULL);
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_ERROR, _efl_io_stdin_event_error, NULL);
return o;
}
EOLIAN static Eina_Error
_efl_io_stdin_efl_io_reader_read(Eo *o, void *pd EINA_UNUSED, Eina_Rw_Slice *rw_slice)
{
Eina_Error ret;
ret = efl_io_reader_read(efl_super(o, MY_CLASS), rw_slice);
if (rw_slice && rw_slice->len > 0)
efl_io_reader_can_read_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "read" */
return ret;
}
#include "efl_io_stdin.eo.c"

View File

@ -0,0 +1,14 @@
class Efl.Io.Stdin (Efl.Loop.Fd, Efl.Io.Reader.Fd) {
[[Application's standard input (stdin).
@since 1.19
]]
data: null;
implements {
Efl.Object.finalize;
Efl.Loop.Fd.fd.set;
Efl.Io.Reader.read;
}
}

View File

@ -0,0 +1,59 @@
#define EFL_IO_WRITER_FD_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_STDOUT_CLASS
static void
_efl_io_stdout_event_write(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_writer_can_write_set(event->object, EINA_TRUE);
}
static void
_efl_io_stdout_event_error(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_writer_can_write_set(event->object, EINA_FALSE);
}
EOLIAN static void
_efl_io_stdout_efl_loop_fd_fd_set(Eo *o, void *pd EINA_UNUSED, int fd)
{
efl_loop_fd_file_set(efl_super(o, MY_CLASS), fd);
efl_io_writer_fd_writer_fd_set(o, fd);
}
EOLIAN static Efl_Object *
_efl_io_stdout_efl_object_finalize(Eo *o, void *pd EINA_UNUSED)
{
int fd = efl_loop_fd_get(o);
if (fd < 0) efl_loop_fd_set(o, STDOUT_FILENO);
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
// TODO: only register "write" if "can_write" is being monitored?
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_WRITE, _efl_io_stdout_event_write, NULL);
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_ERROR, _efl_io_stdout_event_error, NULL);
return o;
}
EOLIAN static Eina_Error
_efl_io_stdout_efl_io_writer_write(Eo *o, void *pd EINA_UNUSED, Eina_Slice *ro_slice, Eina_Slice *remaining)
{
Eina_Error ret;
ret = efl_io_writer_write(efl_super(o, MY_CLASS), ro_slice, remaining);
if (ro_slice && ro_slice->len > 0)
efl_io_writer_can_write_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "write" */
return ret;
}
#include "efl_io_stdout.eo.c"

View File

@ -0,0 +1,14 @@
class Efl.Io.Stdout (Efl.Loop.Fd, Efl.Io.Writer.Fd) {
[[Application's standard output (stdout).
@since 1.19
]]
data: null;
implements {
Efl.Object.finalize;
Efl.Loop.Fd.fd.set;
Efl.Io.Writer.write;
}
}

View File

@ -0,0 +1,88 @@
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_WRITER_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_IO_WRITER_FD_CLASS
typedef struct _Efl_Io_Writer_Fd_Data
{
int fd;
Eina_Bool can_write;
} Efl_Io_Writer_Fd_Data;
EOLIAN static void
_efl_io_writer_fd_writer_fd_set(Eo *o EINA_UNUSED, Efl_Io_Writer_Fd_Data *pd, int fd)
{
pd->fd = fd;
}
EOLIAN static int
_efl_io_writer_fd_writer_fd_get(Eo *o EINA_UNUSED, Efl_Io_Writer_Fd_Data *pd)
{
return pd->fd;
}
EOLIAN static Eina_Error
_efl_io_writer_fd_efl_io_writer_write(Eo *o, Efl_Io_Writer_Fd_Data *pd EINA_UNUSED, Eina_Slice *ro_slice, Eina_Slice *remaining)
{
int fd = efl_io_writer_fd_writer_fd_get(o);
ssize_t r;
EINA_SAFETY_ON_NULL_RETURN_VAL(ro_slice, EINVAL);
if (fd < 0) goto error;
do
{
r = write(fd, ro_slice->mem, ro_slice->len);
if (r < 0)
{
if (errno == EINTR) continue;
if (remaining) *remaining = *ro_slice;
ro_slice->len = 0;
ro_slice->mem = NULL;
if (errno == EAGAIN) efl_io_writer_can_write_set(o, EINA_FALSE);
return errno;
}
}
while (r < 0);
if (remaining)
{
remaining->len = ro_slice->len - r;
remaining->bytes = ro_slice->bytes + r;
}
ro_slice->len = r;
if (r == 0) efl_io_writer_can_write_set(o, EINA_FALSE);
return 0;
error:
if (remaining) *remaining = *ro_slice;
ro_slice->len = 0;
ro_slice->mem = NULL;
return EINVAL;
}
EOLIAN static Eina_Bool
_efl_io_writer_fd_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Io_Writer_Fd_Data *pd)
{
return pd->can_write;
}
EOLIAN static void
_efl_io_writer_fd_efl_io_writer_can_write_set(Eo *o, Efl_Io_Writer_Fd_Data *pd, Eina_Bool can_write)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_writer_fd_writer_fd_get(o) < 0);
if (pd->can_write == can_write) return;
pd->can_write = can_write;
efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
}
#include "efl_io_writer_fd.eo.c"

View File

@ -0,0 +1,22 @@
mixin Efl.Io.Writer.Fd (Efl.Io.Writer) {
[[Write fd using write(2).
@since 1.19
]]
methods {
@property writer_fd {
get {}
set @protected {}
values {
fd: int;
}
}
}
implements {
Efl.Io.Writer.write;
Efl.Io.Writer.can_write.get;
Efl.Io.Writer.can_write.set;
}
}

View File

@ -129,6 +129,15 @@ EAPI extern const Efl_Event_Description _EFL_GFX_PATH_CHANGED;
#include "interfaces/efl_input_interface.eo.h"
#include "interfaces/efl_event.eo.h"
/* Input and Output */
#include "interfaces/efl_io_closer.eo.h"
#include "interfaces/efl_io_reader.eo.h"
#include "interfaces/efl_io_writer.eo.h"
#include "interfaces/efl_io_sizer.eo.h"
#include "interfaces/efl_io_positioner.eo.h"
#include "interfaces/efl_io_buffer.eo.h"
#else
#ifndef EFL_NOLEGACY_API_SUPPORT

View File

@ -0,0 +1,487 @@
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#include "config.h"
#include "Efl.h"
#define MY_CLASS EFL_IO_BUFFER_CLASS
typedef struct _Efl_Io_Buffer_Data
{
uint8_t *bytes;
size_t allocated;
size_t used;
size_t limit;
size_t position_read;
size_t position_write;
Eina_Bool closed;
Eina_Bool can_read;
Eina_Bool can_write;
} Efl_Io_Buffer_Data;
static Eina_Bool
_efl_io_buffer_realloc(Eo *o, Efl_Io_Buffer_Data *pd, size_t size)
{
void *tmp;
size_t limit = efl_io_buffer_limit_get(o);
if ((limit > 0) && (size > limit))
size = limit;
if (pd->allocated == size) return EINA_FALSE;
if (efl_io_sizer_size_get(o) > size)
{
if (efl_io_buffer_position_read_get(o) > size)
efl_io_buffer_position_read_set(o, size);
if (efl_io_buffer_position_write_get(o) > size)
efl_io_buffer_position_write_set(o, size);
/* no efl_io_sizer_size_set() since it could recurse! */
pd->used = size;
efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
}
if (size == 0)
{
free(pd->bytes);
tmp = NULL;
}
else
{
tmp = realloc(pd->bytes, size);
EINA_SAFETY_ON_NULL_RETURN_VAL(tmp, EINA_FALSE);
}
pd->bytes = tmp;
pd->allocated = size;
efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL);
return EINA_TRUE;
}
static Eina_Bool
_efl_io_buffer_realloc_rounded(Eo *o, Efl_Io_Buffer_Data *pd, size_t size)
{
if ((size > 0) && (size < 128))
size = ((size / 32) + 1) * 32;
else if (size < 1024)
size = ((size / 128) + 1) * 128;
else if (size < 8192)
size = ((size / 1024) + 1) * 1024;
else
size = ((size / 4096) + 1) * 4096;
return _efl_io_buffer_realloc(o, pd, size);
}
EOLIAN static void
_efl_io_buffer_preallocate(Eo *o, Efl_Io_Buffer_Data *pd, size_t size)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
if (pd->allocated < size)
_efl_io_buffer_realloc_rounded(o, pd, size);
}
EOLIAN static void
_efl_io_buffer_limit_set(Eo *o, Efl_Io_Buffer_Data *pd, size_t limit)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
if (pd->limit == limit) return;
pd->limit = limit;
if (pd->allocated > limit)
_efl_io_buffer_realloc(o, pd, limit);
efl_io_reader_can_read_set(o, efl_io_buffer_position_read_get(o) < efl_io_sizer_size_get(o));
efl_io_writer_can_write_set(o, (limit == 0) ||
(efl_io_buffer_position_write_get(o) < limit));
}
EOLIAN static size_t
_efl_io_buffer_limit_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->limit;
}
EOLIAN static Eina_Bool
_efl_io_buffer_slice_get(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Slice *slice)
{
if (slice)
{
slice->mem = pd->bytes;
slice->len = efl_io_sizer_size_get(o);
}
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE);
return EINA_TRUE;
}
EOLIAN static Eina_Binbuf *
_efl_io_buffer_binbuf_steal(Eo *o, Efl_Io_Buffer_Data *pd)
{
Eina_Binbuf *ret;
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), NULL);
ret = eina_binbuf_manage_new(pd->bytes, efl_io_sizer_size_get(o), EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(ret, NULL);
pd->bytes = NULL;
pd->allocated = 0;
efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL);
efl_io_sizer_resize(o, 0);
return ret;
}
EOLIAN static Efl_Object *
_efl_io_buffer_efl_object_finalize(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED)
{
size_t limit;
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
efl_io_reader_can_read_set(o, efl_io_buffer_position_read_get(o) < efl_io_sizer_size_get(o));
limit = efl_io_buffer_limit_get(o);
efl_io_writer_can_write_set(o, (limit == 0) ||
(efl_io_buffer_position_write_get(o) < limit));
return o;
}
EOLIAN static void
_efl_io_buffer_efl_object_destructor(Eo *o, Efl_Io_Buffer_Data *pd)
{
if (!efl_io_closer_closed_get(o))
efl_io_closer_close(o);
efl_destructor(efl_super(o, MY_CLASS));
if (pd->bytes)
{
free(pd->bytes);
pd->bytes = NULL;
pd->allocated = 0;
pd->used = 0;
pd->position_read = 0;
pd->position_write = 0;
}
}
EOLIAN static Eina_Error
_efl_io_buffer_efl_io_reader_read(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Rw_Slice *rw_slice)
{
Eina_Slice ro_slice;
size_t used, read_pos, available;
EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
used = efl_io_sizer_size_get(o);
read_pos = efl_io_buffer_position_read_get(o);
available = used - read_pos;
if (rw_slice->len > available)
{
rw_slice->len = available;
if (rw_slice->len == 0)
return EAGAIN;
}
ro_slice.len = rw_slice->len;
ro_slice.mem = pd->bytes + read_pos;
*rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
efl_io_buffer_position_read_set(o, read_pos + ro_slice.len);
return 0;
error:
rw_slice->len = 0;
return EINVAL;
}
EOLIAN static Eina_Bool
_efl_io_buffer_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->can_read;
}
EOLIAN static void
_efl_io_buffer_efl_io_reader_can_read_set(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Bool can_read)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
if (pd->can_read == can_read) return;
pd->can_read = can_read;
efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
}
EOLIAN static Eina_Bool
_efl_io_buffer_efl_io_reader_eos_get(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED)
{
return efl_io_closer_closed_get(o) ||
efl_io_buffer_position_read_get(o) >= efl_io_sizer_size_get(o);
}
EOLIAN static void
_efl_io_buffer_efl_io_reader_eos_set(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED, Eina_Bool is_eos)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
if (is_eos)
efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
}
EOLIAN static Eina_Error
_efl_io_buffer_efl_io_writer_write(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
{
size_t available, todo, write_pos, limit;
int err = EINVAL;
EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
write_pos = efl_io_buffer_position_write_get(o);
available = pd->allocated - write_pos;
limit = efl_io_buffer_limit_get(o);
err = ENOSPC;
if (available >= slice->len)
todo = slice->len;
else if ((limit > 0) && (pd->allocated == limit)) goto error;
else
{
_efl_io_buffer_realloc_rounded(o, pd, write_pos + slice->len);
if (pd->allocated >= write_pos + slice->len)
todo = slice->len;
else
todo = pd->allocated - write_pos;
if (todo == 0) goto error;
}
memcpy(pd->bytes + write_pos, slice->mem, todo);
if (remaining)
{
remaining->len = slice->len - todo;
if (remaining->len)
remaining->mem = slice->bytes + todo;
else
remaining->mem = NULL;
}
slice->len = todo;
if (pd->used < write_pos + todo)
{
pd->used = write_pos + todo;
efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
efl_io_reader_can_read_set(o, pd->position_read < pd->used);
}
efl_io_buffer_position_write_set(o, write_pos + todo);
return 0;
error:
if (remaining) *remaining = *slice;
slice->len = 0;
return err;
}
EOLIAN static Eina_Bool
_efl_io_buffer_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->can_write;
}
EOLIAN static void
_efl_io_buffer_efl_io_writer_can_write_set(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Bool can_write)
{
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
if (pd->can_write == can_write) return;
pd->can_write = can_write;
efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
}
EOLIAN static Eina_Error
_efl_io_buffer_efl_io_closer_close(Eo *o, Efl_Io_Buffer_Data *pd)
{
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL);
efl_io_sizer_resize(o, 0);
pd->closed = EINA_TRUE;
efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
return 0;
}
EOLIAN static Eina_Bool
_efl_io_buffer_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->closed;
}
EOLIAN static Eina_Error
_efl_io_buffer_efl_io_sizer_resize(Eo *o, Efl_Io_Buffer_Data *pd, uint64_t size)
{
Eina_Error ret = 0;
Eina_Bool reallocated;
size_t old_size, pos_read, pos_write;
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL);
if (efl_io_sizer_size_get(o) == size) return 0;
old_size = pd->used;
pd->used = size;
efl_event_freeze(o);
reallocated = _efl_io_buffer_realloc_rounded(o, pd, size);
efl_event_thaw(o);
if (size > pd->allocated)
{
pd->used = size = pd->allocated;
ret = ENOSPC;
}
if (old_size < size)
memset(pd->bytes + old_size, 0, size - old_size);
pos_read = efl_io_buffer_position_read_get(o);
if (pos_read > size)
efl_io_buffer_position_read_set(o, size);
else
efl_io_reader_can_read_set(o, pos_read < size);
pos_write = efl_io_buffer_position_write_get(o);
if (pos_write > size)
efl_io_buffer_position_write_set(o, size);
else
{
size_t limit = efl_io_buffer_limit_get(o);
efl_io_writer_can_write_set(o, (limit == 0) || (pos_write < limit));
}
efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
if (reallocated)
efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL);
return ret;
}
EOLIAN static uint64_t
_efl_io_buffer_efl_io_sizer_size_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->used;
}
EOLIAN static Eina_Error
_efl_io_buffer_efl_io_positioner_seek(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED, int64_t offset, Efl_Io_Positioner_Whence whence)
{
size_t size;
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL);
size = efl_io_sizer_size_get(o);
if (whence == EFL_IO_POSITIONER_WHENCE_CURRENT)
{
whence = EFL_IO_POSITIONER_WHENCE_START;
offset += efl_io_positioner_position_get(o);
}
else if (whence == EFL_IO_POSITIONER_WHENCE_END)
{
whence = EFL_IO_POSITIONER_WHENCE_START;
offset += size;
}
EINA_SAFETY_ON_TRUE_RETURN_VAL(whence != EFL_IO_POSITIONER_WHENCE_START, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(offset < 0, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL((size_t)offset > size, EINVAL);
efl_io_buffer_position_read_set(o, offset);
efl_io_buffer_position_write_set(o, offset);
return 0;
}
EOLIAN static uint64_t
_efl_io_buffer_efl_io_positioner_position_get(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED)
{
uint64_t r = efl_io_buffer_position_read_get(o);
uint64_t w = efl_io_buffer_position_write_get(o);
/* if using Efl.Io.Positioner.position, on set it will do both
* read/write to the same offset, however on Efl.Io.Reader.read it
* will only update position_read (and similarly for
* Efl.Io.Writer), thus on the next position.get we want the
* greatest position.
*
* This allows the buffer to be used solely as reader or writer
* without the need to know it have two internal offsets.
*/
if (r >= w)
return r;
return w;
}
EOLIAN static Eina_Bool
_efl_io_buffer_position_read_set(Eo *o, Efl_Io_Buffer_Data *pd, uint64_t position)
{
size_t size;
Eina_Bool changed;
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE);
size = efl_io_sizer_size_get(o);
EINA_SAFETY_ON_TRUE_RETURN_VAL(position > size, EINA_FALSE);
if (pd->position_read == position) return EINA_TRUE;
changed = efl_io_positioner_position_get(o) != position;
pd->position_read = position;
efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_POSITION_READ_CHANGED, NULL);
if (changed)
efl_event_callback_call(o, EFL_IO_POSITIONER_EVENT_POSITION_CHANGED, NULL);
efl_io_reader_can_read_set(o, position < size);
efl_io_reader_eos_set(o, position >= size);
return EINA_TRUE;
}
EOLIAN static uint64_t
_efl_io_buffer_position_read_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->position_read;
}
EOLIAN static Eina_Bool
_efl_io_buffer_position_write_set(Eo *o, Efl_Io_Buffer_Data *pd, uint64_t position)
{
size_t size;
size_t limit;
Eina_Bool changed;
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE);
size = efl_io_sizer_size_get(o);
EINA_SAFETY_ON_TRUE_RETURN_VAL(position > size, EINA_FALSE);
if (pd->position_write == position) return EINA_TRUE;
changed = efl_io_positioner_position_get(o) != position;
pd->position_write = position;
efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_POSITION_WRITE_CHANGED, NULL);
if (changed)
efl_event_callback_call(o, EFL_IO_POSITIONER_EVENT_POSITION_CHANGED, NULL);
limit = efl_io_buffer_limit_get(o);
efl_io_writer_can_write_set(o, (limit == 0) || (position < limit));
return EINA_TRUE;
}
EOLIAN static uint64_t
_efl_io_buffer_position_write_get(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd)
{
return pd->position_write;
}
#include "interfaces/efl_io_buffer.eo.c"

View File

@ -0,0 +1,129 @@
class Efl.Io.Buffer (Efl.Object, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer, Efl.Io.Sizer, Efl.Io.Positioner) {
[[Generic In-memory buffer of data to be used as I/O.
This class offers both input and output, which can be used at
the same time since @Efl.Io.Reader.read and @Efl.Io.Writer.write
use different offsets/position internally.
One can get temporary direct access to internal buffer with
@.slice_get or steal the buffer with @.binbuf_steal.
A fixed sized buffer can be implemented by setting @.limit
followed by @.preallocate
See @Efl.Io.File.
@since 1.19
]]
methods {
preallocate {
[[Immediately pre-allocate a buffer of at least a given size.]]
params {
@in size: size; [[amount of bytes to pre-allocate.]]
}
}
@property limit {
[[Limit how big the buffer can grow.
This affects both @.preallocate and how buffer grows
when @Efl.Io.Writer.write is called.
If you want a buffer of an exact size, always set the
limit before any further calls that can grow it.
]]
get { }
set {
[[Constructor-only property to set buffer limit. 0 is unlimited]]
}
values {
size: size; [[Defines a maximum buffer size, or 0 to allow unlimited amount of bytes]]
}
}
@property position_read {
[[The position used by @Efl.Io.Reader.read.
Note that @Efl.Io.Positioner.seek or
@Efl.Io.Positioner.position.set will affect this property
and @.position_write.
@Efl.Io.Positioner.position.get will return the greatest
of @.position_read and @.position_write.
]]
get { }
set {
return: bool (false);
}
values {
position: uint64;
}
}
@property position_write {
[[The position used by @Efl.Io.Writer.write.
Note that @Efl.Io.Positioner.seek or
@Efl.Io.Positioner.position.set will affect this property
and @.position_read.
@Efl.Io.Positioner.position.get will return the greatest
of @.position_read and @.position_write.
]]
get { }
set {
return: bool (false);
}
values {
position: uint64;
}
}
slice_get { // TODO: property and return of Eina.Slice (not pointer)
[[Get a temporary access to buffer's internal memory.
The memory pointed by slice may be changed by other
methods of this class. The event "reallocated" will be
called in those situations.
]]
params {
@out slice: Eina.Slice; [[slice of the current buffer, may be invalidated if @Efl.Io.Writer.write, @Efl.Io.Closer.close or @Efl.Io.Sizer.resize are called. It is the full slice, not a partial one starting at current position.]]
}
return: bool (false);
}
binbuf_steal {
[[Steals the internal buffer memory and returns it as a binbuf.
The returned memory must be freed with eina_binbuf_free().
]]
return: free(own(Eina.Binbuf*), eina_binbuf_free) @warn_unused;
}
}
events {
position_read,changed; [[Notifies @.position_read changed]]
position_write,changed; [[Notifies @.position_write changed]]
reallocated; [[Notifies the internal buffer was reallocated, thus whatever was returned by @.slice_get becomes invalid]]
}
implements {
Efl.Object.finalize;
Efl.Object.destructor;
Efl.Io.Reader.read;
Efl.Io.Reader.can_read.get;
Efl.Io.Reader.can_read.set;
Efl.Io.Reader.eos.get;
Efl.Io.Reader.eos.set;
Efl.Io.Writer.write;
Efl.Io.Writer.can_write.get;
Efl.Io.Writer.can_write.set;
Efl.Io.Closer.close;
Efl.Io.Closer.closed.get;
Efl.Io.Sizer.resize;
Efl.Io.Sizer.size.get;
Efl.Io.Positioner.seek;
Efl.Io.Positioner.position.get;
}
}

View File

@ -0,0 +1,13 @@
#include "config.h"
#include "Efl.h"
EOLIAN static Eina_Bool
_efl_io_closer_closed_set(Eo *obj, void *pd EINA_UNUSED, Eina_Bool is_closed)
{
if (is_closed)
return efl_io_closer_close(obj) == 0;
return EINA_FALSE;
}
#include "interfaces/efl_io_closer.eo.c"

View File

@ -0,0 +1,49 @@
import eina_types;
mixin Efl.Io.Closer {
[[Generic interface for objects that can close themselves.
This interface allows external objects to transparently close an
input or output stream, cleaning up its resources.
Calls to @.close() may or may not block, that's not up to this
interface to specify.
@since 1.19
]]
data: null;
methods {
close @virtual_pure {
[[Closes the Input/Output object.
This operation will be executed immediately and may or
may not block the caller thread for some time. The
details of blocking behavior is to be defined by the
implementation and may be subject to other parameters
such as non-blocking flags, maximum timeout or even
retry attempts.
You can understand this method as close(2) libc function.
]]
return: Eina.Error; [[0 on succeed, a mapping of errno otherwise]]
}
@property closed {
[[If true will notify object was closed.]]
get @virtual_pure { }
set {
[[If true, calls close()]]
return: bool; [[$true if could close, $false if already closed or errors.]]
}
values {
is_closed: bool;
}
}
}
events {
closed; [[Notifies closed, when property is marked as true]]
}
}

View File

@ -0,0 +1,10 @@
#include "config.h"
#include "Efl.h"
EOLIAN static Eina_Bool
_efl_io_positioner_position_set(Eo *o, void *pd EINA_UNUSED, uint64_t position)
{
return efl_io_positioner_seek(o, position, EFL_IO_POSITIONER_WHENCE_START) == 0;
}
#include "interfaces/efl_io_positioner.eo.c"

View File

@ -0,0 +1,41 @@
import eina_types;
enum Efl.Io.Positioner.Whence {
start, [[seek from start of the stream/file]]
current, [[seek from current position]]
end, [[seek from the end of stream/file]]
}
mixin Efl.Io.Positioner {
[[Generic interface for objects that can change or report position.
@since 1.19
]]
data: null;
methods {
seek @virtual_pure {
params {
@in offset: int64; [[offset in byte relative to whence]]
@in whence: Efl.Io.Positioner.Whence;
}
return: Eina.Error; [[0 on succeed, a mapping of errno otherwise]]
}
@property position {
get @virtual_pure { }
set {
[[Try to set position object, relative to start of file. See @.seek()]]
return: bool; [[$true if could reposition, $false if errors.]]
}
values {
position: uint64;
}
}
}
events {
position,changed; [[Notifies position changed]]
}
}

View File

@ -0,0 +1,5 @@
#define EFL_IO_READER_PROTECTED 1
#include "config.h"
#include "Efl.h"
#include "interfaces/efl_io_reader.eo.c"

View File

@ -0,0 +1,80 @@
import eina_types;
interface Efl.Io.Reader {
[[Generic interface for objects that can read data into a provided memory.
This interface allows external objects to transparently monitor
for new data and as it to be read into a provided memory slice.
Calls to @.read may or may not block, that's not up to this
interface to specify. The user can check based on @.eos property
and signal if the stream reached an end, with event
"can_read,changed" or property @.can_read to known whenever a read
would have data to return.
@since 1.19
]]
methods {
read {
[[Reads data into a pre-allocated buffer.
This operation will be executed immediately and may or
may not block the caller thread for some time. The
details of blocking behavior is to be defined by the
implementation and may be subject to other parameters
such as non-blocking flags, maximum timeout or even
retry attempts.
You can understand this method as read(2) libc function.
]]
params {
@inout rw_slice: Eina.Rw_Slice @nonull; [[provides a pre-allocated memory to be filled up to rw_slice.len. It will be populated and the length will be set to the actually used amount of bytes, which can be smaller than the request.]]
}
return: Eina.Error; [[0 on succeed, a mapping of errno otherwise]]
}
@property can_read {
[[If $true will notify @.read can be called without blocking or failing.]]
get { }
set @protected { }
values {
can_read: bool;
}
}
@property eos {
[[If $true will notify end of stream.]]
get { }
set @protected { }
values {
is_eos: bool;
}
}
}
events {
can_read,changed; [[Notifies can_read property changed.
If @.can_read is $true there is data to
@.read without blocking/error. If
@.can_read is $false, @.read would either
block or fail.
Note that usually this event is dispatched
from inside @Efl.Io.Reader.read, thus
before it returns.
]]
eos; [[Notifies end of stream, when property is marked as true.
If this is used alongside with an @Efl.Io.Closer, then
it should be emitted before that call.
It should be emitted only once for an object unless it
implements @Efl.Io.Positioner.seek.
The property @.can_read should change to $false before
this event is dispatched.
]]
}
}

View File

@ -0,0 +1,10 @@
#include "config.h"
#include "Efl.h"
EOLIAN static Eina_Bool
_efl_io_sizer_size_set(Eo *o, void *pd EINA_UNUSED, uint64_t size)
{
return efl_io_sizer_resize(o, size) == 0;
}
#include "interfaces/efl_io_sizer.eo.c"

View File

@ -0,0 +1,37 @@
import eina_types;
mixin Efl.Io.Sizer {
[[Generic interface for objects that can resize or report size of themselves.
This interface allows external objects to transparently resize
or report its size.
@since 1.19
]]
data: null;
methods {
resize @virtual_pure {
params {
@in size: uint64;
}
return: Eina.Error; [[0 on succeed, a mapping of errno otherwise]]
}
@property size {
get @virtual_pure { }
set {
[[Try to resize the object, check with get if the value was accepted or not.]]
return: bool; [[$true if could resize, $false if errors.]]
}
values {
size: uint64;
}
}
}
events {
size,changed; [[Notifies size changed]]
}
}

View File

@ -0,0 +1,5 @@
#define EFL_IO_WRITER_PROTECTED 1
#include "config.h"
#include "Efl.h"
#include "interfaces/efl_io_writer.eo.c"

View File

@ -0,0 +1,62 @@
import eina_types;
interface Efl.Io.Writer {
[[Generic interface for objects that can write data from a provided memory.
This interface allows external objects to transparently write
data to this object and be notified if more data can be written
or it's topped capacity.
Calls to @.write() may or may not block, that's not up to this
interface to specify. The user can check with event
"can_write,changed" or property @.can_write to known whenever a write
could push more data.
@since 1.19
]]
methods {
write {
[[Writes data from a pre-populated buffer.
This operation will be executed immediately and may or
may not block the caller thread for some time. The
details of blocking behavior is to be defined by the
implementation and may be subject to other parameters
such as non-blocking flags, maximum timeout or even
retry attempts.
You can understand this method as write(2) libc function.
]]
params {
@inout slice: Eina.Slice @nonull; [[provides a pre-populated memory to be used up to slice.len. The returned slice will be adapted as length will be set to the actually used amount of bytes, which can be smaller than the request.]]
@out remaining: Eina.Slice @optional; [[convenience to output the remaining parts of slice that was not written. If the full slice was written, this will be a slice of zero-length.]]
}
return: Eina.Error; [[0 on succeed, a mapping of errno otherwise]]
}
@property can_write {
[[If $true will notify @.write can be called without blocking or failing.]]
get { }
set @protected { }
values {
can_write: bool;
}
}
}
events {
can_write,changed; [[Notifies can_write property changed.
If @.can_write is $true there is data to
@.write without blocking/error. If
@.can_write is $false, @.write would
either block or fail.
Note that usually this event is
dispatched from inside
@Efl.Io.Writer.write, thus before it
returns.
]]
}
}